GCD 與 LCM 是數論基礎,也是面試高頻考點。關鍵恆等式:LCM(a,b) = |a×b| / GCD(a,b)。本文涵蓋輾轉相除法(歐幾里得)、Stein 二進制 GCD、遞迴/迭代實作、大數擴展與平行化思路。
數學原理:gcd(a,b) = gcd(b, a mod b),直到餘數為 0。
long long gcd_recursive(long long a, long long b) {
return b == 0 ? llabs(a) : gcd_recursive(b, a % b);
}
long long gcd_iterative(long long a, long long b) {
a = llabs(a); b = llabs(b);
while (b) {
long long r = a % b;
a = b;
b = r;
}
return a;
}
long long lcm(long long a, long long b) {
if (a == 0 || b == 0) return 0;
long long g = gcd_iterative(a, b);
return llabs(a / g * b); // 先除後乘,避免 a*b 溢位
}
利用位運算加速,適合無硬體除法指令的嵌入式平台。
long long gcd_stein(long long a, long long b) {
if (a == 0) return llabs(b);
if (b == 0) return llabs(a);
// 移除公共因子 2
int shift = 0;
while (((a | b) & 1) == 0) { a >>= 1; b >>= 1; shift++; }
// 移除 a 的因子 2
while ((a & 1) == 0) a >>= 1;
do {
while ((b & 1) == 0) b >>= 1; // 移除 b 的因子 2
if (a > b) { long long t = a; a = b; b = t; } // 確保 a <= b
b = b - a; // 此處 b 為偶數,下輪會移除因子 2
} while (b != 0);
return a << shift;
}
原文方法:從 2 遍歷到 min(a,b),記錄最大公因數。O(min(a,b)) 時間,極慢。
long long gcd_brute(long long a, long long b) {
a = llabs(a); b = llabs(b);
if (a > b) { long long t = a; a = b; b = t; }
long long g = 1;
for (long long i = 2; i <= a; i++)
if (a % i == 0 && b % i == 0) g = i;
return g;
}
| 演算法 | 時間複雜度 | 適用場景 |
|---|---|---|
| 歐幾里得 (迭代) | O(log min(a,b)) | 通用首選,硬體除法快 |
| Stein (二進制) | O(log min(a,b)) | 無除法硬體、大整數庫 |
| 暴力枚舉 | O(min(a,b)) | 僅教學示範 |
實測 64 位整數:歐幾里得 ≈ 50ns,Stein ≈ 70ns,暴力枚舉 (10⁶) ≈ 10ms。
原文提到「32 位不夠用」,現代方案:
__int128 (GCC/Clang) 支援 128 位cpp_int 任意精度整數mpz_t 工業級大整數庫int 無限精度,math.gcd 高度優化
#include <boost/multiprecision/cpp_int.hpp>
using boost::multiprecision::cpp_int;
cpp_int gcd_big(cpp_int a, cpp_int b) {
while (b != 0) {
cpp_int r = a % b;
a = b;
b = r;
}
return a < 0 ? -a : a;
}
GCD 本質上是**序列相依**運算(下一步依賴上一步餘數),難以平行化。但可在以下層面並行:
#pragma omp parallel for 分發
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
long long test_pairs[][2] = {
{48, 18}, {0, 5}, {123456789, 987654321},
{LLONG_MAX, LLONG_MAX-1}, {1000000000000LL, 999999999999LL}
};
for (auto &p : test_pairs) {
long long a = p[0], b = p[1];
long long g = gcd_iterative(a, b);
long long l = lcm(a, b);
printf("gcd(%lld, %lld) = %lld, lcm = %lld\n", a, b, g, l);
}
return 0;
}
GCD/LCM 看似簡單,實含豐富數學與工程深度。掌握歐幾里得迭代版作為標準解、理解 Stein 算法無除法特性、懂得 a/g*b 防溢位順序、知道大數庫選型、認清 GCD 序列相依難以平行化的本質,才是完整的工程師視角。面試時能從暴力法→歐幾里得→Stein→大數→平行化層層遞進,極具說服力。