码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-08-31 14:43:22      阅读:101      评论:0      收藏:0      [点我收藏+]

标签:大整数   greatest   test   turn   最大的   编程之美   奇数   使用   else   

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

任意给定两个数字,得到其最大公约数 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);
    }
}
技术图片

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

标签:大整数   greatest   test   turn   最大的   编程之美   奇数   使用   else   

原文地址:https://www.cnblogs.com/twoheads/p/11438804.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!