// Version 1int gcd(int a, int b) { if (b == 0) return a; return gcd(b, a % b);}// Version 2int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
Java
// Version 1public int gcd(int a, int b) { if (b == 0) return a; return gcd(b, a % b);}// Version 2public int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b);}
Python
def gcd(a, b): if b == 0: return a return gcd(b, a % b)
递归至 b == 0(即上一步的 a % b == 0)的情况再返回值即可。
根据上述递归求法,我们也可以写出一个迭代求法:
[list2tab]
C++
int gcd(int a, int b) { while (b != 0) { int tmp = a; a = b; b = tmp % b; } return a;}
Java
public int gcd(int a, int b) { while(b != 0) { int tmp = a; a = b; b = tmp % b; } return a;}
Python
def gcd(a, b): while b != 0: a, b = b, a % b return a
Big gcd(Big a, Big b) { if (a == 0) return b; if (b == 0) return a; // 记录a和b的公因数2出现次数,countr_zero表示二进制末位0的个数 int atimes = countr_zero(a); int btimes = countr_zero(b); int mintimes = min(atimes, btimes); a >>= atimes; for (;;) { // a和b公因数中的2已经计算过了,后面不可能出现a为偶数的情况 b >>= btimes; // 确保 a<=b if (a > b) swap(a, b); b -= a; if (b == 0) break; btimes = countr_zero(b); } return a << mintimes;}
上述代码参考了 libstdc++ 和 MSVC 对 C++17 std::gcd 的实现。在 unsigned int 和 unsigned long long 的数据范围下,如果可以以极快的速度计算 countr_zero,则 Stein 算法比欧几里得算法来得快,但反之则可能比欧几里得算法慢。
// 以小端序实现的二进制 Big,要求能枚举每一个元素int countr_zero(Big a) { int ans = 0; for (auto x : a) { if (x != 0) { ans += 32; // 每一位数据类型的位长 } else { return ans + countr_zero(x); } } return ans;}// 暴力计算,如需使用建议直接写进 gcd 加快常数int countr_zero(Big a) { int ans = 0; while ((a & 1) == 0) { a >>= 1; ++ans; } return ans;}
int Exgcd(int a, int b, int &x, int &y) { if (!b) { x = 1; y = 0; return a; } int d = Exgcd(b, a % b, x, y); int t = x; x = y; y = t - (a / b) * y; return d;}
Python
def Exgcd(a, b): if b == 0: return a, 1, 0 d, x, y = Exgcd(b, a % b) return d, y, x - (a // b) * y
函数返回的值为 gcd,在这个过程中计算 x,y 即可。
值域分析
ax+by=gcd(a,b) 的解有无数个,显然其中有的解会爆 long long。
万幸的是,若 b=0,扩展欧几里得算法求出的可行解必有 ∣x∣≤b,∣y∣≤a。
下面给出这一性质的证明。
int exgcd(int a, int b, int &x, int &y) { int x1 = 1, x2 = 0, x3 = 0, x4 = 1; while (b != 0) { int c = a / b; std::tie(x1, x2, x3, x4, a, b) = std::make_tuple(x3, x4, x1 - x3 * c, x2 - x4 * c, b, a - b * c); } x = x1, y = x2; return a;}