1、辗转相除法
这条算法基于一个定理:两个正整数 a 和 b ( a > b),它们的最大公约数等于 a 除以 b 的余数 c 和 b 之间的最大公约数。例如 10 和 25,25 除以 10 商 2 余 5,那么 10 和 25 的最大公约数,等同于 10 和 5 的最大公约数。
public int getGreatestCommonDivisor(int a,int b){
int big = a > b ? a:b;
int small = a < b ? a : b;
if(big % small == 0){
return small;
}
return getGreatestCommonDivisor(big % small,small);
}
缺点:当2个数都比较大的时候,取模操作的性能会比较差。
2、 更相减损术
原理:两个正整数 a 和 b (a>b),它们的最大公约数等于 a - b 的差值 c 和较小数 b 的最大公约数。例如 10 和 25,25 减 10 的差是15,那么 10 和 25 的最大公约数,等同于 10 和 15 的最大公约数。
public int getGreatestCommonDivisor(int a,int b){
if(a == b){
return a;
}
int big = a > b ? a:b;
int small = a < b ? a : b;
return getGreatestCommonDivisor(big-small,small);
}
缺点:当两数相差较大,如1和99999,则递归次数远大于辗转相除法,是个不稳定的算法。
3、在更相减损术的基础上使用移位运算
当 a 和 b 均为偶数时,gcd(a,b) = 2 × gcd(a/2,b/2) =2 × gcd(a≫1,b≫1)。
当 a 为偶数,b 为奇数时,gcd(a,b)=gcd(a/2,b)=gcd(a≫1,b)。
当 a 为奇数,b 为偶数时,gcd(a,b)=gcd(a,b/2)=gcd(a,b≫1)。
当 a 和 b 均为奇数时,先利用更相减损术运算一次,gcd(a,b) = gcd(b,a−b), 此时 a-b 必然是偶数,然后又可以继续进行移位运算。
例如计算 10 和 25 的最大公约数的步骤如下。
整数 10 通过移位,可以转换成求 5 和 25 的最大公约数。
利用更相减损术,计算出 25-5=20,转换成求 5 和 20 的最大公约数。
整数 20 通过移位,可以转换成求 5和 10 的最大公约数。
整数 10 通过移位,可以转换成求 5 和 5 的最大公约数。
利用更相减损术,因为两数相等,所以最大公约数是 5。
这种方式在两数都比较小时,可能看不出计算次数的优势;当两数越大时,计算次数的减少就会越明显。
public int gcd(int a, int b){
if(a == b){
return a;
}
if((a&1)==0 && (b&1)==0){
return gcd(a>>1, b>>1)<<1;
} else if((a&1)==0 && (b&1)!=0){
return gcd(a>>1, b);
} else if((a&1)!=0 && (b&1)==0){
return gcd(a, b>>1);
} else {
int big = a>b ? a:b;
int small = a<b ? a:b;
return gcd(big-small, small);
}
}
1、暴力枚举法:时间复杂度是 O(min(a,b))。
2、辗转相除法:时间复杂度不太好计算,可以近似为O(log(max(a,b))),但是取模运算性能较差。
3、更相减损术:避免了取模运算,但是算法性能不稳定,最坏时间复杂度为 O(max(a,b))。
4、更相减损术与移位相结合:不但避免了取模运算,而且算法性能稳定,时间复杂度为 O(log(max(a,b)))。
作者:小灰
链接:https://leetcode-cn.com/leetbook/read/journey-of-algorithm/52c1mm/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。