【数论】二进制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)就好了

posted @ 2016-11-21 20:53  wxjor  阅读(1100)  评论(0编辑  收藏  举报