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