exgcd求ax + by = gcd(a, b)中x和y的通解(下面简称通解)
/*
什么是通解
我们知道二元一次方程, 是如果只有一个式子, 那么解会有无数个
而通解就是指让我们只找到一个解就可以推出其他所有解的式子
(注意本证明极其复杂, 请直接背模版或感性理解)
知道了定义下面就是推式子了
首先设x, y是ax + by = gcd(a, b)的一个解
那么有y = (gcd(a, b) - ax) / b (1), 且y, x, a, b是整数
再设一个解x0 = x + k (2), k和x0是整数,
那么有 a*x0 + b*y0 = gcd(a, b);
y0 = (gcd(a, b) - a*x0) / b (3)
由(3)和(2)得
y0 = (gcd(a, b) - ax - ak)/b <==> y0 = (gcd(a, b) - ax)/b - a/b * k (4)
由(4)和(1)得 y0 = y - a/b * k (5)
我们要保证y0是整数, y又是整数, 那么a/b * k就是整数
设d = gcd(a, b);
a = a' * d, b = b' * d, 这里a', b'互质(非常基本的知识点, 如果他们不互质, d就可以
通过a', b'的公约数变大, 这就和我们d是最大公约数就矛盾了, 所以a'和b'互质)
a/b * k == a'/b' * k,
因为a'和b'互质, 那么a'/b' * k 要是想是整数的话, k只能是b'的倍数(k == 0时整个就是
0也是整数, 满足要求)
不然就消不掉a'/b'的分母b', 就会产生小数, 就不是整数了
so, k = n*b', (n是任意整数),
又因为b' * d = b ==> b' = b / d
这样k = n*b/d
所以x0 = x + n*b/d (6)
通过(5)和(6)可得 y0 = y - n*a/d (7) // 这里是-, 不是+
至此通解就出来了
x0 = x + n*b/d
y0 = y - n*a/d
更常见的是这样的
x0 = x + k*b/d (当然这里的k不是上面的k, 是和n一样的任意整数)
y0 = y - k*a/d
附带最小正整数解就是 x % (b / d);
因为会有x是负数的情况, 所以更准确的应该是
(x % (b/d) + b/d) % (b/d)
就相当于把所有的k*b/d给删掉, 剩下的就是最小的正整数解
感谢https://blog.csdn.net/qq_41779251/article/details/82634045这篇文章
*/
203. 同余方程 - AcWing题库
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
int n, m;
int exgcd(int a, int b, LL &x, LL &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
LL x, y, d;
cin >> n >> m;
d = exgcd(n, m, x, y);
cout << (x % m + m) % m << endl; // 这里d是1所以不除也没事
return 0;
}
补充
exgcd二元一次等式, 扩大后判断是否有整数解,(小数解都是有的, 但是小数解不符合我们要求)
首先exgcd求出的等式ax + by = d肯定是有整数解的, 但是等式两边扩大非整数倍后就不一定了, 一定得是整数倍,
如果是小数比如1/2就不一定对了, 比如6x + 2y = 2合法, 但是6*x0 + 2*y0 = 1就是无整数解情况(x变为x/2 == x0, 对于后式是求x0, 而非x)
ax + by = k, k满足扩大d的整数倍的条件就是 k % d == 0, k = n * d
k % d == 0这就是扩大后判断是否有整数解的充要条件
我们知道通解是x = x0 + k*b/gcd(a, b), 设m = gcd(a, b), t = d / gcd(a, b)
等式扩大了, 但是通解不变, 因为a没变, b没变, a(x*t + k*b/m) + b(y*t - k*a/m) = d
axt + bxt + akb/m - bka/m = d
axt + bxt = d, 可以发现等式两边没变
也就说明通解依旧可行, 对于其他情况也可以这样证明, 通解的可行性