辗转相除法
问题:给出两个数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)))


浙公网安备 33010602011771号