求最大公约数的几种方法

Posted by Wengdada on May 7, 2017

介绍了求最大公约数的几种方法,蛮力法,辗转相除法,更相减损术,位移+更相减损术,有兴趣的可以参考 漫画算法:辗转相除法是什么鬼?,下面给出了几种方法的java实现。

代码如下:

/* 求最大公约数
 * 参考--漫画算法:辗转相除法是什么鬼?
 * http://blog.jobbole.com/106315/
 * eclipse 自动导入  ctrl+shift+o
 */
import java.util.Scanner;
public class GCD {

    public static void main(String[] args) {
        Scanner s=new Scanner(System.in);
        int a=s.nextInt();
        int b=s.nextInt();
        int result1=brute_gcd(a,b);
        int result2=Euclidean_gcd(a,b);
        int result3=Decrease_gcd(a, b);
        int result4=Bit_shift_gcd(a, b);
        System.out.println("枚举法得到的结果:"+result1);
        System.out.println("辗转相除法得到的结果:"+result2);
        System.out.println("更相减损得到的结果:"+result3);
        System.out.println("位移法得到的结果:"+result4);
        
    }
    
    //暴力枚举的方法,试图寻找到一个合适的整数 i,看看这个整数能否被两个整型参数a和b同时整除。
    //当a=10000,b=10001时效果不好
    //这里的话为了方便使得a总是大于b,如果不是,再递归一次(b,a),下面的其他方法也是这样子
    public static int brute_gcd(int a,int b){
        
         if(a>=b){
            if(a%b==0)return b;
            int result = 1;
            for(int i=2;i<=b/2;i++){
                if( a%i==0 && b%i==0 ){
                    result=i;
                }   
            }
            return result;
        }
    
        else return brute_gcd(b,a);
    }
    
    
    
    //Euclidean algorithm 欧几里得算法,又叫辗转相除法
    //两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。
    //逐渐把两个较大整数之间的运算简化成两个较小整数之间的运算,直到两个数可以整除,或者其中一个数减小到1为止。
    //同样当两个整数较大时取模运算的性能比较低,所以用减法来代替,有了更相减损术
    public static int Euclidean_gcd(int a,int b){
        
        if(a>=b){
            if(a%b==0)return b;
            else return Euclidean_gcd(a%b,b);
        }
        
        else return Euclidean_gcd(b,a);

    }
    
    
    //更相减损术, 出自于中国古代的《九章算术》,也是一种求最大公约数的算法。
    //原理更加简单:两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。
    //逐渐把两个较大整数之间的运算简化成两个较小整数之间的运算,直到两个数可以相等为止,最大公约数就是最终相等的两个数。
    //这种方法也是有问题的,当a,b相差很大时如10000,2,就要递归很多次,继续优化,在此基础上利用位移
    public static int Decrease_gcd(int a,int b){
        
        if(a==b){
            return a;
        }
        
        else if(a>b){
            return Euclidean_gcd(a-b,b);
        }
        else{
            return Euclidean_gcd(b,a);
        }
    }
    
    /*
          移位运算的性能非常快。对于给定的正整数a和b,不难得到如下的结论。其中gcb(a,b)的意思是a,b的最大公约数函数:
          当a和b均为偶数,gcb(a,b) = 2*gcb(a/2, b/2) = 2*gcb(a>>1, b>>1)
          当a为偶数,b为奇数,gcb(a,b) = gcb(a/2, b) = gcb(a>>1, b)
          当a为奇数,b为偶数,gcb(a,b) = gcb(a, b/2) = gcb(a, b>>1)
          当a和b均为奇数,利用更相减损术运算一次,gcb(a,b) = gcb(b, a-b), 此时a-b必然是偶数,又可以继续进行移位运算。        
     */
    public static int Bit_shift_gcd(int a,int b){
        
        if(a==b)return a;
        
        else if(a>b){   
            //if(!((a&1)==0)&&(!(b&0x1)==0)){}来判断是否为偶数,其实是不对的,
            //判断偶数奇数就需要a&1==0(a为偶数)或者a&1==1(a为奇数)就可以了
            if(((a&1)==0)&&((b&0x1)==0)){
                return Bit_shift_gcd(a>>1,b>>1)<<1;
            }
            else if(((a&1)==0)&&((b&1)==1)){
                return Bit_shift_gcd(a>>1,b);
            }
            else if(((a&1)==1)&&((b&1)==0)){
                return Bit_shift_gcd(a,b>>1);
            }
            return Bit_shift_gcd(b,a-b);
        }   
        
        else return Bit_shift_gcd(b,a);
    }
    

}