求最大公约数
今天学习了一下求最大公约数的各种方法,及时总结。
Q:写一段代码,求两个数的最大公约数,尽量优化算法性能
获取最大公约数方法常被称为 getGreatestCommonDivisor(gcd)
1.0 暴力枚举
public int gcd(int a,int b){
int big = Math.max(a,b);
int small = Math.min(a,b);
if(big%small == 0){
return small;
}
for(int i = small/2; i > 1 ; i--){
if(small%i == 0 && big%i == 0){
return i;
}
}
return 1;
}
- 这种写法效率低下,没有运用古人的智慧 O(∩_∩)O
- 复杂度O(min(a,b))
2.0 辗转相除法(欧几里得算法)
欧几里得算法是用来求两个正整数最大公约数的算法。古希腊数学家欧几里得在其著作《The Elements》中最早描述了这种算法,所以被命名为欧几里得算法。
老外方法的核心原理: 两个正整数a和b(a > b),它们的最大公约数等于 a 除以b的余数c 和 b 之间的最大公约数。即:
gcd(a,b) = gcd(a % b , b)
public static int gcd(int a,int b){
int big = Math.max(a,b);
int small = Math.min(a,b);
if(big % small == 0){
return small;
}
return gcd(big % small , small);
}
- 不适用于两数均较大的情况,会导致 a%b 取模运算性能变差。
- 取模运算性能较差,复杂度近似为O(log(max(a,b)))
3.0 更相减损术
国产方法核心原理:两个正整数a和b(a > b),它们的最大公约数等于 a - b 的差值c 和较小数b 的最大公约数。即:
gcd(a,b) = gcd(a - b , b)
public static int gcd(int a,int b){
if(a == b){
return a;
}
int big = Math.max(a,b);
int small = Math.min(a,b);
return gcd(big - small , small);
}
- 不适用于两数大小差距悬殊的情况。例如 a = 10000,b = 1的情况
- 不稳定,最坏情况下复杂度为O(max(a,b))
4.0 中外结合版
核心原理: 结合2.0和3.0以及位运算。
public static int gcd(int a,int b){
if(a == b){
return a;
}
if((a&1)==0 && (b&1)==0){
//a和b均为偶数时, gcd(a,b) = 2*gcd(a/2,b/2);
return gcd(a>>1 , b>>1)<<1;
}else if((a&1)==0 && (b&1)!=0){
//a偶b奇时, gcd(a,b) = gcd(a/2,b);
return gcd(a>>1 , b);
}else if((a&1)!=0 && (b&1)==0){
return gcd(a , b>>1);
}else {
//a和b均为奇数时,先使用更相减损术
int big = Math.max(a,b);
int small = Math.min(a,b);
return gcd(big - small , small);
}
}
- 通过位运算避免了取模运算性能较差的问题
- 性能稳定
- 复杂度为O(log(max(a,b)))