编程之美---最大公约数

该文出自于编程之美中关于最大公约数问题一章。

任意给定两个数字,得到其最大公约数 GCD(greatest common divisor),如果两个数字都很大怎么解决。

分析:最大公约数早在公元前300年,欧几里得的《几何原本》里就提出了一个高效率算法---辗转相除法。

解法一:

假设f(x,y)表示x,y的最大公约数,取k=x/y,b=x%y,则x=ky+b,如果一个数字能同时整除x,y,那么必能够同时整除b,y;而能够同时整除b,y的数必能够同时整除x,y,即x,y的公约数与b,y的公约数是相同的,其最大公约数也是相同的,则必有f(x,y)=f(y,x%y)(x>=y>0),从而把两个数字的最大公约数转化为求两个更小数字的最大公约数,直到其中一个数字为0,剩下的另一个数字就是两者最大的公约数。

例如: f(42,30) = f(30,12) = f(12,6) = f(6,0) = 6

代码如下:

int gcd(int x,int y)
{
    return (!y)?x:gcd(y,x%y);
}

解法二:

由于辗转相除法中存在一个最大的问题是,取模运算(其中用到了除法运算)是非常昂贵的开销,成为了算法的瓶颈。根据解法一的思路分析,假设一个数能够同时整除x,y,则必能够同时整除x-y,y;而同时能够整除x-y,y的数字也必能够同时整除x,y,即x,y的公约数与x-y,y的公约数相同,其最大公约数也是相同的 即:f(x,y) = f(x-y,y)。这样做就不需要进行大量的去摸运算,而转化为减法运算。

例如:f(42,30) = f(30,12) = f(12,18) = f(12,6) = f(6,6,)=f(6,0) = 6

代码如下:

复制代码
int gcd(int x,int y)
{
    if (x < y)
    return gcd(y,x);
    if (y == 0)
    return x;
    return gcd(x-y,y);
}
复制代码

解法三:

解法一处理大整数整除问题比较复杂,解法二处理虽然将大整数问题除法转换为减法运算,降低了复杂度,但是他的问题在于减法迭代次数太多了。最好的方法就是解法一和解法二结合使用。

分析: 对于 y和x来说,如果y = k*y1, x = k*x1。那么有 f(y,x)=k*(y1,x1)。另外,如果x = p*x1,假设p是素数,且y%p!=0(即y不能够被p整除),那么f(x,y)=f(p*x1,y)=f(x1,y);根据以上两点,我们可以对算法进行改进,最简单的就是取素数2,因为对于素数2同时可以转化为移位运算,避免大整数除法。

取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/2,y) = f(x,y>>1);

若:x,y均为奇数,f(x,y)=f(y,x-y),那么就存在f(x,y)=f(y,x-y)之后,(x-y)是一个偶数,下一步一定会有除以2的操作了。

因此这样的复杂度为 O(log2(max(x,y)));

例如:

f(42,30) = f(1010102,111102)

      =2*f(101012,11112)

      =2*f(11112,1102)

      =2*f(11112,112)

      =2*f(11002,112)

      =2*f(112,112)

              =2*f(02,112)

     = 2*112

       =6

代码如下:

复制代码
//奇数 偶数判断
bool isEven(int n)
{
    return (n&1)?false:true;
}
//高效率gcd
int gcd(int x,int 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);
    }
}
复制代码
posted @ 2019-08-31 13:59  twoheads  阅读(682)  评论(0编辑  收藏  举报