exgcd扩展欧几里得
裴蜀定理:对于一对正整数a, b, 存在非零整数x, y使得ax + by = gcd(a, b);
ax + by 一定是最大公约数gcd(x, y)的倍数。
1) 如果b = 0,gcd(a, 0) = a, a就是最大公约数ax + by = a. (x = 1, y = 0是一组解) 0和x的最大公约数就是x。
2)ax + by = gcd(a,b) = gcd(b, a % b)
by + a % b x = by + (a - a / b * b)x = ax + b(y - a / b * x) = ax + by;
x不变,y = y - a / b * x;
所以gcd(a, b, x, y)->gcd(b, a % b, y, x )
#include <iostream> 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, y, x); y -= a / b * x; return d; } int main() { int n; cin>>n; while(n--) { int a,b,x,y; cin>>a>>b; exgcd(a,b,x,y); cout<<x<<" "<<y<<endl; } }
扩展欧几里得可以求解线性同余方程:
a ∗ x ≡ b (mod m)
ax和b对m的余数相同,简称同余。实际上对于两个数a和b,若存在一个数 m | (a - b), 那么 a ≡ b (mod m)
a ∗ x ≡ b (mod m)
存在一个y,使得 ax = my + b, -> ax - my = b, 令y’ = -y, 则有 ax + my’ = b, 就相当于上面的ax + by = gcd(a,b);这里的 b = m;
#include <cstdio> typedef long long LL; 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, y, x); y -= a / b * x; return d; } int main() { int n; scanf("%d",&n); while(n--) { int a,b,m; scanf("%d%d%d",&a,&b,&m); int x, y; int d = exgcd(a,m,x,y); if(b % d) puts("impossible"); else printf("%d\n", x * b / d); } }
这里的b一定等于求得的d = gcd(a,m)最大公约数的倍数,得到倍数以后再让求得的原来的系数x乘以这个倍数就得到答案了。
204. 表达整数的奇怪方式
#include <iostream> #include <algorithm> using namespace std; typedef long long LL; LL exgcd(LL a, LL b, LL &x, LL &y) { if(!b) { x = 1, y = 0; return a; } LL d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } int main() { int n; cin>>n; bool has_answer = true; LL a1, m1; cin>>a1>>m1;//先读入第一个方程,然后把后面的所有方程合并进来 for(int i = 0; i < n - 1; i++) { LL a2, m2; cin>>a2>>m2; LL k1, k2; LL d = exgcd(a1, a2, k1, k2); if((m2 - m1) % d) { has_answer = false; break; } k1 *= (m2 - m1) / d; LL t = a2 / d; k1 = (k1 % t + t) % t;//把k1变成最小的正整数解 //合并之后新的x = ka + m的解 m1 = a1 * k1 + m1; a1 = abs(a1 / d * a2);//这两部顺序不能变 } if(has_answer) { cout<<(m1 % a1 + a1) % a1<<endl; } else puts("-1"); }
x mod a1 = m1, x mod a2 = m2, x mod a3 = m3, ……,x mod an = mn
我们来试着进行合并,先合并第一和第二个式子:
x = k1a1 + m1, x = k2a2 + m2
k1a1 + m1 = k2a2 + m2 ——>>> k1a1 - k2a2 = m2 - m1;
通过exgcd扩展欧几里得可以求出一组解(k1, k2),int d = exgcd(a1, a2, k1, k2)返回的是最大公约数,然后 (m2 - m1) / d是扩大的倍数,如果d | (m2 - m1)能够整除就有解,如果不能整除的话就不能没有解。
构造一个式子:k1 = k1 + k * a2 / d; k2 = k2 + k * a1 / d; 将这个式子带入上面的方程有:
(k1 + k*a2/d)a1 - (k2 + k*a1/d)a2 = m2 - m1; k1a1 - k2a2 = m2 - m1;所以是完全等价的。
所以又有k1 ≡ k1 (mod a2/d), k1 = k1 % (a2 / d), 对一个数%m等于将去整数k倍的m。
带入 x = k1a1 + m1得到:
x = (k1 + k*a2 / d)a1 + m1 = k1a1 + m1 + k*a1*a2 / d;
令a = a1*a2 /d, m = k1a1 + m1, 那么通过两个式子合并之后:x = ka + m, x ≡ m (mod a), x mod a ≡ m, 那么 x = m % a。
最后a会变成a1, a2, a3,……,an这n个数的公倍数,C++中余数可以为负,-5 % 3 = -2, 可以转化为:(-5 % 3 + 3) % 3 = 1.
中国剩余定理:
m1, m2, m3,……,mk两两互质。x ≡ a (mod m).
x ≡ a1 (mod m1), x ≡ a2(mod m2), x ≡ a3(mod m3),……,x ≡ ak(mod mk)
令M = m1*m2*……mk, Mi = M / mi(除了mi之外的其他所有m的乘积)所以Mi与mi互质,那么Mi * Mi^-1 ≡ 1 (mod mi),互质的两个数肯定有逆元,
Mi^-1(Mi % mi的逆元)形如 ax ≡ 1 (mod m)
通解是: x = (a1 * M1 * M1^-1 + a2M2M2^-1+……+akMkMk^-1)% m1 = a1, 因为M1*M1^-1 mod m1 = 1, 后面的所有Mi都有m1的质因子,所以%m1都为0.