zoukankan      html  css  js  c++  java
  • Stein算法求最大公约数

    首先引进一个符号:gcd是greatest common divisor(最大公约数)的缩写,gcd( x,y ) 表示x和y的最大公约数。然后有一个事实需要了解:一个奇数的所有约数都是奇数。这个很容易,下面我们要用到。 
        来研究一下最大公约数的性质,我们发现有 gcd( k*x,k*y ) = k*gcd( x,y ) 这么一个非常好的性质(证明我就省去了)。说他好是因为他非常符合我们化小的思想。我们试取 k=2 ,则有 gcd( 2x,2y ) = 2 * gcd( x,y )。这使我们很快联想到将两个偶数化小的方法。那么一奇一个偶以及两个奇数的情况我们如何化小呢?

        先来看看一奇一偶的情况: 设有2x和y两个数,其中y为奇数。因为y的所有约数都是奇数,所以 a = gcd( 2x,y ) 是奇数。根据2x是个偶数不难联想到,a应该是x的约数。我们来证明一下:(2x)%a=0,设2x=n*a,因为a是奇数,2x是偶数,则必有n是偶数。又因为 x=(n/2)*a,所以 x%a=0,即a是x的约数。因为a也是y的约数,所以a是x和y的公约数,有 gcd( 2x,y ) <= gcd( x,y )。因为gcd( x,y )明显是2x和y的公约数,又有gcd( x,y ) <= gcd( 2x,y ),所以 gcd( 2x,y ) = gcd( x,y )。至此,我们得出了一奇一偶时化小的方法。

        再来看看两个奇数的情况:设有两个奇数x和y,似乎x和y直接向小转化没有什么太好的办法,我们可以绕个道,把x和y向偶数靠拢去化小。不妨设x>y,我们注意到x+y和x-y是两个偶数,则有 gcd( x+y,x-y ) = 2 * gcd( (x+y)/2,(x-y)/2 ),那么 gcd( x,y ) 与 gcd( x+y,x-y ) 以及 gcd( (x+y)/2,(x-y)/2 ) 之间是不是有某种联系呢?为了方便我设 m=(x+y)/2 ,n=(x-y)/2 ,容易发现 m+n=x ,m-n=y 。设 a = gcd( m,n ),则 m%a=0,n%a=0 ,所以 (m+n)%a=0,(m-n)%a=0 ,即 x%a=0 ,y%a=0 ,所以a是x和y的公约数,有 gcd( m,n )<= gcd(x,y)。再设 b = gcd( x,y )肯定为奇数,则 x%b=0,y%b=0 ,所以 (x+y)%b=0 ,(x-y)%b=0 ,又因为x+y和x-y都是偶数,跟前面一奇一偶时证明a是x的约数的方法相同,有 ((x+y)/2)%b=0,((x-y)/2)%b=0 ,即 m%b=0 ,n%b=0 ,所以b是m和n的公约数,有 gcd( x,y ) <= gcd( m,n )。所以 gcd( x,y ) = gcd( m,n ) = gcd( (x+y)/2,(x-y)/2 )。

    我们来整理一下,对两个正整数 x>y :
    1.均为偶数 gcd( x,y ) =2gcd( x/2,y/2 );
    2.均为奇数 gcd( x,y ) = gcd( (x+y)/2,(x-y)/2 );
    2.x奇y偶   gcd( x,y ) = gcd( x,y/2 );
    3.x偶y奇   gcd( x,y ) = gcd( x/2,y )  或 gcd( x,y )=gcd( y,x/2 );

    现在我们已经有了递归式,还需要再找出一个退化情况。注意到 gcd( x,x ) = x ,我们就用这个。

    #include <cstdio>
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    
    int Stein(int x,int y)
    {
        int ans = 0,tmp;
        if(x<y){
            swap(x,y);
        }
        if(y==0) return x;//0能被任何非0数整除
        while(x!=y)
        {
            if(x&1){//x&0x1(0x表示16#)
                if(y&1){//x,y同为奇数时
                    y = (x-y)>>1;
                    x -= y;
                }else{//x为奇数,y为偶数时
                    y>>=1;
                }
            }else{
                if(y&1){//x为偶数,y为奇数
                    x>>=1;
                    if(x<y) swap(x,y);
                }else{//x,y都为偶数
                    x>>=1;
                    y>>=1;
                    ans++;
                }
            }
        }
        return x<<ans;
    }
    int main()
    {
        //freopen("input.txt","r",stdin);
        int x,y;
    
        while(~scanf("%d%d",&x,&y))
        {
            int ans = Stein(x,y);
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    nio的学习
    并发编程学习(二)
    并发编程学习(一)
    linux基础知识-常用命令
    linux基础知识-目录结构
    springcloud的config
    zuul学习
    hystrix学习
    feign学习
    ribbon学习
  • 原文地址:https://www.cnblogs.com/qie-wei/p/10160170.html
Copyright © 2011-2022 走看看