• 编程之美---最大公约数问题


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

    任意给定两个数字,得到其最大公约数 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);
        }
    }
  • 相关阅读:
    VS2015 update3 安装 asp.net core 失败
    connection timeout 和command timeout
    安装.NET Core
    xamarin 学习笔记02- IOS Simulator for windows 安装
    xamarin 学习笔记01-环境配置
    BotFramework学习-02
    BotFramework学习-01
    正则表达式
    获取指定字节长度的字符串
    pdf生成器
  • 原文地址:https://www.cnblogs.com/newpanderking/p/3953223.html
Copyright © 2020-2023  润新知