几种求最大公约数的方法
欧几里得算法(辗转相除法)
使用
使用该算法,可以递归取模,迭代到a能被b整除时,即可得最大公约数b。
证明
代码实现
public static int gcd(int a, int b) {
int r = a % b;
if (r != 0) {
return gcd(b, r);
}
return b;
}
时间复杂度
如果,则余数小于b,故成立。
如果,余数即为,故成立。
更相减损术(尼考曼彻斯法,辗转相减法)
更相减损术是出自《九章算术》中的一种约分的方法,也可以用来求最大公约数。
可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。
使用
使用该方法计算最大公约数,假设,求 的最大公约数即是求 的最大公约数,递归到当两个数相等时,该数即为 的最大公约数。
证明
代码实现
public static int gcd(int a, int b) {
if (a < b) {
return gcd(b, a);
} else if (a == b) {
return a;
} else {
return gcd(b, a - b);
}
}
时间复杂度
辗转相减法和辗转相除法思路很类似,但如果当一个数很大,另一个数很小时,需要很多次减法才能达到一次除法的效果,使得此方法的时间复杂度退为,而辗转相除法则稳定于 。
Stein算法(更相减损术优化版)
辗转相减法的缺点在于如果一个数很大,另一个数很小时,迭代次数会比较多。
Stein算法则对辗转相减法做了一些优化:
使用
在更相减损法中,若两个是偶数则同除以2,结果乘以2。若为一奇一偶则偶数除以2,结果不变(奇数必然不会被2约分)。若为两个奇数才相减。
详细流程参考代码
证明
只是辗转相减法的优化,证明参考辗转相减法。
代码实现
public static int gcd(int a, int b) {
int k = 1;
while ((a & 1) == 0 && (b & 1) == 0) {
a = a >> 1;
b = b >> 1;
k = k << 1;
}
while (a != b) {
while ((a & 1) == 0) {
a = a >> 1;
}
while ((b & 1) == 0) {
b = b >> 1;
}
if (a == b) {
break;
}
if (a < b) {
b = b - a;
} else {
int c = b;
b = a - b;
a = c;
}
}
return a * k;
}
//使用递归的方式实现
public static int gcd(int a, int b) {
return _gcd(a, b, 1);
}
private static int _gcd(int a, int b, int k) {
if ((a & 1) == 0 && (b & 1) == 0) {
return _gcd(a >> 1, b >> 1, k << 1);
} else if ((a & 1) == 0) {
a = a >> 1;
} else if ((b & 1) == 0) {
b = b >> 1;
}
if (a == b) {
return a * k;
} else if (a > b) {
return _gcd(b, a - b, k);
} else {
return _gcd(a, b - a, k);
}
}
时间复杂度
与辗转相除法相比,每次迭代的运算要更简单,特别是数字较大时的取模运算可能会比较耗时,因此Stein算法的性能在一定程度上会更优秀一些。