最大公约数问题

来源:互联网 发布:java 静态代理原理 编辑:程序博客网 时间:2024/05/17 02:08

编程之美有一道关于求最大公约数的题目,该书的作者提供了三种思路:
第一种解法:
根据辗转相除法,可以得到f(x,y) = f(y , x % y) (x >= y > 0),直到其中一个数为0,这公式可以由以下三条推论得到:

  • 推论1、如果a能被b整除(a=qb),若k为正整数,则ka也能被b整除(ka=kqb)。
  • 推论2、如果a能被c整除(a=hc),b也能被c整除(b=tc),则(a±b)也能被c整除。
  • 推论3、如果a能被b整除(a=qb),b也能被a整除(b=ta),则a=b 因为:a=qb b=ta a=qta qt=1 因为q、t均为正整数,所以t=q=1,所以:a=b。

示例如下:
f(42,30) = f(30,12) = f(12,6) = f(6,0) = 6
整个算法过程需要多次的取模运算,而取模运算在计算机里面是个非常昂贵运算的开销。
第二种解法:
利用解法一的三条推论也可以得到f(x,y)=f(x-y,y),其中(x>y),直到其中一个数为0,示例如下:
f(42,30)=f(30,12)=f(12,18)=f(18,12)=f(12,6)=f(6,6)=f(6,0)=6.
整个算法过程虽然用了减法免去了除法运算的开销,但是增加其迭代次数。
第三种解法:
对于y和x来说,如果y = k * y1, x = k * x1。那么有f(y , x) = k * f(y , x),另外,我们知道每个非素数的数字都可以拆成几个因子相乘得到,如果要求两个的数的公约数,其实变相于寻找两个数的相同因子,所以当x = p * x1,(p是素数)并且y % p != 0,那么就有f(x , y) = f( p * x1, y) = f(x1, y)。
假设取p = 2,就有以下几种情况:

  • 若 x, y均为偶数,f (x,y) = 2 * f(x / 2, y / 2) = 2 * f(x >> 1, y >> 1)
  • 若x为偶,而y为奇,f (x , y ) = f (x / 2, y) = f ( x >> 1, y)
  • 若x为奇,y为偶,f ( x, y) = f (x , y / 2) = f(x , y >> 1)
  • 若x,y均为奇,f ( x, y ) = f (y , x - y)

其中f ( x, y ) = f (y , x - y)之后,(x-y)x-y)是一个偶数,下一步一定会有除以2的整数,这个算法过程的时间复杂度最坏为O(log2 (max(x,y))).
例如:

f(42,30)=f(1010102,111102)=2f(101012,11112)=2f(11112,1102)=2f(11112,112)=2f(11002,112)=2f(112,112)=2f(02,112)=2112=6

代码实现:

#include <iostream>#include<stdio.h>#include<stdlib.h>using namespace std;//判断是否偶数#define ISEVEN(y) !(y%2)typedef  long long LGLG;LGLG gcd(LGLG x,LGLG y){   //交换大小   if(x<y)      return gcd(y,x);   //边界   if(y==0)      return x;         if(ISEVEN(x))      {         //第一种情况         if(ISEVEN(y))         {            return (gcd(x>>1,y>>1)<<1);         }         //第二种情况         else         {            return gcd(x>>1,y);         }      }   else      {         //第三种情况         if(ISEVEN(y))         {            return gcd(x,y>>1);         }         //第四种情况         else         {            return gcd(y,x-y);         }      }}int main(){   //测试   for(int i=0;i<10;i++)   {      LGLG x=0,y=0;      //生成数的范围[0,50]      x=rand()%50+1;      y=rand()%50+1;      cout<<x<<"\t"<<y<<endl;      cout<<"result is:  "<<gcd(x,y)<<endl;   }    return 0;}

整个算法过程中利用移位和加减法运算即避开除法又减少了迭代次数,提高了其算法效率。
参考资料:
辗转相除法证明

0 0
原创粉丝点击