• 【数论】二进制GCD


    二进制GCD

        GCD这种通用的算法相信每个OLER都会 ,辗转相除,代码只有四行 :

    int GCD(int a,int b){
        if(b==0) return a;
        return GCD(b,a%b);
    }
    

      GCD算法使通过辗转相除法来求解两个数的最大公因数,又称欧几里得算法

         可以知道:GCD(x,y)=GCD(x,y-x)

         我们将b能被a整除记作a|b

         那么假设z是最大公因数,那么有:

                如果z|x,z|y,则z|(y-x)  (因为x和y肯定可以写作a*z=x,b*z=y,那么a*z-b*z=(a-b)*z,一定可以整除)

         那么再设z不是x的因子,则z不是x和y-x的公因子

         设z|x.z不是y的因子,则z不是x和y-x的公因子

         那么代码就是上面那个啦!

    如果想进一步提高这个算法的效率,那么我们可以选择二进制GCD

        我们可以通过不断地筛去因子2来提高算法的效率,这样的2可以是公共的或单个的,总之不影响算法的正确性

        那么为什么不是筛去因子3、因子4呢?

        因为计算机只提供2进制的快速运算(按位),所以判断a%2=?0可以直接写成!(a&1),但是其它数是没有的,我们知道计算机做取模运算的效率是很低很低的。

        那下面我们来看一看证明过程:

        GCD(x,y)=x   (x==y)

        GCD(x,y)=2*(GCD(x/2,y/2))  (!(x&1) and !(y&1))

        GCD(x,y)=GCD(x/2,y) (!(x&1) and (y&1)    因为2显然不是公因数,所以我们可以果断地筛掉它)

        GCD(x,y)=GCD(x,y/2) ((x&1) and !(y&1)    理由同上)

        GCD(x,y)=GCD(x-y,y) (辗转相减) 

        那么通过上面的推理,我们可以得出代码:

    int GCD(int x,int y){
        int i=0,j=0;
        if(x==0) return y;//if和for一定不能反,要么会炸
        if(y==0) return x;//一个没用了就返回另一个
        for(i;0==(x&1);i++) x>>=1;//化简为n*(m^2)形式
        for(j;0==(y&1);j++) y>>=1;//化简为a*(b^2)形式
        if(i>j) i=j;//去最大 公 因数,当然是你有我有的了
        while(1){
            if(x<y) x^=y,y^=x,x^=y;//二进制交换,非常高级
            if(0==(x-=y)) return y<<i;//那么就把以前的次幂乘上去,辗转减操作
            while(0==(x&1)) x>>=1;//x减了y以后可能还是成为a*(b^2)形式,要继续筛去
        }
    }
    

      

        至于LCM(最小公倍数)来说有如下定理:

        x*y=LCM(x,y)*GCD(x,y),那么只需要求出GCD以后算一下x*y/GCD(x,y)就好了

  • 相关阅读:
    转载1
    转载
    WampServer的研究日记一
    第一期 花式自适应网页哪家强? 就选你啦
    缓动函数requestAnimationFrame用法
    原生js canvas 碰撞游戏的开发笔记2
    非常便利的前端模板smarty js 的使用
    原生js canvas 碰撞游戏的开发笔记
    Sublime Text 的研究日记
    面向对象设计模式(目录)
  • 原文地址:https://www.cnblogs.com/wxjor/p/6086929.html
Copyright © 2020-2023  润新知