辗转相除法
有了这条定理,求最大公约数就变得简单了。我们可以使用递归的方法把问题逐步简化。
首先,计算出 a 除以 b 的余数 c ,把问题转化成求 b 和 c 的最大公约数;然后计算出 b 除以 c 的余数 d ,把问题转化成求 c 和 d 的最大公约数;再计算出 c 除以 d 的余数 e ,把问题转化成求 d 和 e 的最大公约数……
以此类推,逐渐把两个较大整数之间的运算简化成两个较小整数之间的运算,直到两个数可以整除,或者其中一个数减小到 1 为止。
public static int getGreatestCommonDivisor(int a, int b) {
int big = Math.max(a, b);
int small = Math.min(a, b);
if (big % small == 0) {
return small;
}
return getGreatestCommonDivisor(big % small, small);
}
有一个问题,当两个整数较大时,做 a%b 取模运算的性能会比较差。
更相减损术
原理:。
例如 10 和 25,25 减 10的差是 15,那么 10 和 25 的最大公约数,等同于 10 和 15 的最大公约数。
由此,我们同样可以通过递归来简化问题。首先,计算出 a 和 b 的差值 c (假设 a >b),把问题转化成求 b 和 c 的最大公约数;然后计算出 c 和 b 的差值 d (假设 c >b),把问题转化成求 b 和 d 的最大公约数;再计算出 b 和 d 的差值 e (假设 b >d),把问题转化成求 d 和 e 的最大公约数……
以此类推,逐渐把两个较大整数之间的运算简化成两个较小整数之间的运算,直到两个数可以相等为止,最大公约数就是最终相等的这两个数的值。
public static int getGreatestCommonDivisor(int a, int b) {
if (a == b) return a;
int big = Math.max(a, b);
int small = Math.min(a, b);
return getGreatestCommonDivisor(big - small, small);
}
更相减损术是不稳定的算法,当两数相差悬殊时,如计算 10000 和 1 的最大公约数,就要递归 9999 次!
位运算
对于给出的正整数 a 和 b ,不难得到如下的结论。
(从下文开始,获得最大公约数的方法getGreatestCommonDivisor被简写为gcd。)
- 当 a 和 b 均为偶数时,
。
- 当 a 为偶数,b 为奇数时,
。
- 当 a 为奇数,b 为偶数时,
。
- 当 a 和 b 均为奇数时,先利用更相减损术运算一次,
,此时 a-b 必然是偶数,然后又可以继续进行移位运算。
public static 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 = Math.max(a, b);
int small = Math.min(a, b);
return gcd(big - small, small);
}
}