[数学] exgcd 学习笔记
Part 1:ax + by = gcd(a, b)
裴蜀定理:对于任意整数 \(a\),\(b\),方程 \(ax + by = \gcd(a, b)\) 有解。
如何证明?
考虑辗转相除法。设 \(\gcd(a, b) = d\)。
最后一步(\(b = 0\))时,\(d = a\),所以显然有 \(a \times 1 + b \times 0 = d\)。
此时 \(x = 1\),\(y = 0\)。
之后呢?考虑数学归纳法!
假设我们求得了方程 \(bx + (a \bmod b)y = \gcd(b, a \bmod b)\) 的解,解为 \(x\) 和 \(y\)。
而我们要解的这个方程 \(ax + by = \gcd(a, b)\) 的解为 \(x^{\prime}\) 和 \(y^{\prime}\)。
如何转移?
首先可以把 \((a \bmod b)\) 改写成 \(a - \lfloor \frac{a}{b} \rfloor \times b\)。
接着,根据辗转相除法,\(\gcd(b, a \bmod b) = \gcd(a, b) = d\)。
然后展开: $$bx + (a \bmod b)y = \gcd(b, a \bmod b) = d$$
\[bx + (a - \lfloor \frac{a}{b} \rfloor \times b) \times y = d \]\[bx + ay - b \lfloor \frac{a}{b} \rfloor \times y = d \]\[ay + b(x - \lfloor \frac{a}{b} \rfloor \times y) = d \]所以,\(x^{\prime} = y\),\(y^{\prime} = (x - \lfloor \frac{a}{b} \rfloor \times y)\)!
这样我们不仅证明了这个定理,还搞出来了一套算法来求出一组解。
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) { x = 1, y = 0; return a; }
int d = exgcd(b, a % b, x, y);
int z = x; x = y, y = z - y * (a / b);
return d;
}
这个函数的返回值就是 \(\gcd(a,b)\)。
使用方法如下。d = exgcd(a, b, x, y);这时的 \(x\) 和 \(y\) 就是解。
Part 2:ax + by = c
考虑方程 \(ax + by = c\),这个如何求解?
设方程 \(ax + by = \gcd(a,b) = d\) 的解为 \(x_0\) 和 \(y_0\)。
则它的特解是 \(x = \frac{c}{d} x_0\),\(y = \frac{c}{d} y_0\)。
而通解是 \(x = \frac{c}{d} x_0 + k \frac{b}{d}\),\(y = \frac{c}{d} y_0 - k \frac{a}{d}\)。
大部分时候,我们要求正整数解,所以可以解个不等式搞出来 \(k\) 的范围。
代码如下。
int k1 = -(c / d * x) / (b / d); // k >= k1
if (c / d * x + b / d * k1 <= 0) k1++;
int k2 = (c / d * y) / (a / d); // k <= k2
if (c / d * y - a / d * k2 <= 0) k2--;
搞出来的范围就是 \(k_1 \le k \le k_2\),如果 \(k_2 < k_1\) 就说明没有正整数解。
如果要求更加详细,参考 P5656 【模板】二元一次不定方程 (exgcd)。
代码如下。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int exgcd(int a, int b, int &x, int &y) {
if (b == 0) { x = 1, y = 0; return a; }
int d = exgcd(b, a % b, x, y);
int z = x; x = y, y = z - y * (a / b);
return d;
}
int T, a, b, c, d, x, y;
signed main() {
cin >> T;
while (T--) {
cin >> a >> b >> c;
d = exgcd(a, b, x, y);
if (c % d == 0) {
int k1 = -(c / d * x) / (b / d); // k >= k1
if (c / d * x + b / d * k1 <= 0) k1++;
int k2 = (c / d * y) / (a / d); // k <= k2
if (c / d * y - a / d * k2 <= 0) k2--;
if (k1 > k2) {
cout << c / d * x + b / d * k1 << " ";
cout << c / d * y - a / d * k2 << " ";
} else {
cout << k2 - k1 + 1 << " ";
cout << c / d * x + b / d * k1 << " ";
cout << c / d * y - a / d * k2 << " ";
cout << c / d * x + b / d * k2 << " ";
cout << c / d * y - a / d * k1 << " ";
}
} else cout << "-1";
cout << "\n";
}
return 0;
}
Part 3:ax = b (mod m)
可以转化为 \(ax + my = b\)。
Part 4:ax = 1 (mod m)
可以转化为 \(ax + my = 1\)。
这个方法求逆元不需要保证 \(m\) 是质数,只要有逆元就可以求,还是非常强大的。

浙公网安备 33010602011771号