[数学] 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\) 是质数,只要有逆元就可以求,还是非常强大的。

posted @ 2026-08-14 18:06  ZeroToDream  阅读(9)  评论(0)    收藏  举报