求两个数的最大公约数,辗转相除法与更相减损法(递归迭代)

时间:2021-01-26 00:33:21

问题:给出两个数a和b,求出他们的最大公约数(greatest common divisor)。

解法一:辗转相除法,又叫欧几里得算法。两个正整数a和b(a>b),他们的最大公约数等于a除以b的余数和b之间的最大公约数。

比如10和25,25除以10余5,那么10和25的最大公约数等同于5和10之间的最大公约数。

//辗转相除法    递归解法
int gcd(int a,int b){
    if(a%b==0)
        return b;
    return (b,a%b);
}

 

//辗转相除法   迭代解法
int
gcd2(int a,int b){ int t; while(b!=0){ t=b; b=a%b; a=t; } return a; }

 

解法二:更相减损术,出自中国古代的《九章算术》。两个正整数a和b(a>b),他们的最大公约数等于a-b的差值c和较小数b的最大公约数。

比如10和25,25-10=15,那么10和25的最大公约数等于10和15的最大公约数。

//更相减损术  递归 
int gcd3(int a,int b){
    if(a==b)
        return a;
    if(a>b)
        return gcd(a-b,b);
    else
        return gcd(b-a,a); 
}

 

//更相减损术  迭代
int gcd4(int a,int b){
    while(a*b!=0){
        if(a>b)
            a=a-b;
        else
            b=b-a;    
    }
    return a?a:b;
}

 

改进:更相减损术是不稳定的算法,当两个数相差悬殊时,如10000和1的最大公约数,要递归9999次。

做法;

当a和b都是偶数时,gcd(a,b)=2*gcd(a/2,b/2)=2*gcd(a>>1,b>>1)。

当a是奇数,b是偶数时,gcd(a,b)=gcd(a,b/2)=gcd(a,b>>1)。

当a是偶数,b是奇数时,gcd(a,b)=gcd(a/2,b)=gcd(a>>1,b)。

当a和b都是奇数数时,gcd(a,b)=gcd(a-b,b)。

//改进版:
int gcd5(int a,int b){
    if(a==b)
        return a;
    else if(a<b)
        return gcd5(b,a);    //始终让 a>b
    else{
        if(!(a&1)&&!(b&1)){    //两数都为偶数 
            return gcd5(a>>1,b>>1)<<1;
        }else if((a&1)&&!(b&1)){    //a奇数 b偶数 
            return gcd5(a,b>>1);
        }else if(!(a&1)&&(b&1)){    //a偶数 b奇数 
            return gcd5(a>>1,b);
        }else
            return gcd(a,a-b);        //都是奇数 相减 
        
    } 
}

算法复杂度:

辗转相除法:O(log(max(a,b)))

更相减损法:O(max(a,b))

改进更相减损法:O(log(max(a,b)))