輾轉相除法(歐幾里得算法)
歐幾里德算法又稱輾轉相除法,是指用於計算兩個正整數a,b的最大公約數。
時間復雜度為\(O(logN)\)。
舉例
比如:30和42的最大公約數:
\(30 \mod 42 = 30\)
\(42 \mod 30 = 12\)
\(30 \mod 12 = 6\)
\(12 \mod 6 = 0\)
那么,30和42的最大公約數就是6。
代碼實現
int gcd(int a, int b) {
while (b > 0) {
c = a % b;
a = b;
b = c;
}
return a;
}
我們也可以使用遞歸的方法來實現。
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
性能分析
由於輾轉相除法的時間復雜度為\(O(logN)\),在遇到比較大的數時(\(N\ge{10^{256}}\)),計算起來就會比較慢了。
這是因為輾轉相除法中含有模運算,當\(a\mod{b}\)時,相當於\(a\)減了\(k\)次\(b\),也就是\(a - k * b\)。
比如:
\(42 \mod 30 = 42 - 1 * 30\)
\(30 \mod 12 = 30 - 2 * 12\)
更相減損術
更相減損術是出自《九章算術》的一種求最大公約數的算法,它原本是為約分而設計的,但它適用於任何需要求最大公約數的場合。
最壞時間復雜度為\(O(N)\)。
舉例
當\(a>b\)時,用\(a-b\);當\(b>a\)時,用\(b-a\)。
\(\gcd(30, 42) = \gcd(12, 30) = \gcd(18, 12) = \gcd(6, 12) = \gcd(6, 6)\)
當\(a=b\)時,\(a\)就是這兩個數的最大公約數。
代碼實現
int gcd(int a, int b) {
if (a == b) return a;
else if (a > b) return gcd(a - b, b);
else if (a < b) return gcd(b - a, a);
}
更相減損術二分版
上文提到,在兩個差距非常大的數(\(a=10000, b=1\)),使用更相減損術的時間復雜度為\(O(N)\)。
這個算法的時間復雜度為\(O(logN)\)。
代碼實現
可以使用按位與(&)來代替模運算。
例如:\((5)_{10} = (101)_{2}\),我們可以使用5&1
來達到和模運算符一樣的效果。
我們知道,按位左移1位就是乘2,右移一位就是除以2。
int gcd(int a, int b) {
if (a == b) return a;
if ((a & 1) && (b & 1)) return gcd(a >> 1, b >> 1) << 2; // a和b都是偶數
else if ((a & 1) && !(b & 1)) return gcd(a >> 1, b); // a是偶數b是奇數
else if (!(a & 1) && (b & 1)) return gcd(a, b >> 1); // a是奇數b是偶數
else if (!(a & 1) && !(b & 1)) { // a和b都是奇數
if (a > b) return gcd(a - b, b);
else if (a < b) return gcd(b - a, a);
}
}
參考
感謝Vita小老師~
【算法小知識】如何求最大公約數(上)
【算法小知識】如何求最大公約數(下)