扩展欧几里得定理
扩展欧几里得算法(Extended Euclidean Algorithm,简称 ExGCD)是数论中非常核心的算法。在处理组合数学问题、计算多重集排列组合的取模运算时,它通常是计算乘法逆元的首选工具。普通的欧几里得算法(辗转相除法)只能用来求两个数的最大公约数:\(\gcd(a, b) = \gcd(b, a \bmod b)\)。而扩展欧几里得算法不仅能求出 \(\gcd(a, b)\),还能额外求出满足裴蜀定理(Bézout's identity)的两个整数 \(x\) 和 \(y\),使得:
思路
这个算法的核心思想是递归和状态转移。边界情况(递归到底层): 当 \(b = 0\) 时,\(\gcd(a, 0) = a\)。此时我们需要满足等式 \(a \cdot x + 0 \cdot y = a\)。显然,这组解非常直观:\(x = 1, y = 0\)。回溯过程(状态转移):假设我们已经递归进入下一层,求出了关于 \(b\) 和 \(a \bmod b\) 的方程的解 \(x_1\) 和 \(y_1\),即:$$b \cdot x_1 + (a \bmod b) \cdot y_1 = \gcd(a, b)$$在 C++ 等编程语言中,取模运算可以通过整除来表示:\(a \bmod b = a - \lfloor a/b \rfloor \cdot b\)。我们把这个替换掉上面等式中的 \(a \bmod b\):$$b \cdot x_1 + (a - \lfloor a/b \rfloor \cdot b) \cdot y_1 = \gcd(a, b)$$现在,我们将这个式子展开,并把含有 \(a\) 和含有 \(b\) 的项重新合并提取公因式:$$a \cdot y_1 + b \cdot (x_1 - \lfloor a/b \rfloor \cdot y_1) = \gcd(a, b)$$最后,将这个展开后的式子与我们原本要求的最高层等式 \(ax + by = \gcd(a, b)\) 进行对比。对应系数相等,就可以得到当前层的 \(x\) 和 \(y\) 与下一层的 \(x_1\) 和 \(y_1\) 之间的递推关系:\(x = y_1\) \(y = x_1 - \lfloor a/b \rfloor \cdot y_1\)
模板代码
#include <iostream>
// 函数返回值为 a 和 b 的最大公约数 (gcd)
// 参数 x 和 y 通过引用传递,执行完毕后将保存满足 ax + by = gcd(a,b) 的解
int exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1;
y = 0;
return a; // 边界情况:gcd(a, 0) = a
}
int x1, y1;
// 递归调用,先求出下一层的解 x1 和 y1
int d = exgcd(b, a % b, x1, y1);
// 根据推导出的状态转移方程更新当前层的解
x = y1;
y = x1 - (a / b) * y1;
return d; // 向上层层传递最大公约数
}
int main() {
int a = 120, b = 23;
int x, y;
int gcd = exgcd(a, b, x, y);
std::cout << "gcd(" << a << ", " << b << ") = " << gcd << "\n";
std::cout << a << " * (" << x << ") + " << b << " * (" << y << ") = " << gcd << "\n";
return 0;
}

浙公网安备 33010602011771号