扩展欧几里得
食用范围
给定 n n n 对正整数 a i , b i a_i,b_i ai,bi,对于每对数,求出一组 x i , y i x_i,y_i xi,yi ,使其满足 a i ∗ x i + b i ∗ y i = g c d ( a i , b i ) a_i∗x_i+b_i∗y_i=gcd(a_i,b_i) ai∗xi+bi∗yi=gcd(ai,bi)
此等式存在的条件: g c d ( a , m ) ∣ b gcd(a,m)|b gcd(a,m)∣b 成立
裴蜀定理
对于任意正整数 a , b a,b a,b ,那么一定存在非零整数 x , y x,y x,y 使得 a x + b y = m i n ( g c d ( a , b ) ) ax+by=min(gcd(a,b)) ax+by=min(gcd(a,b))
求这个 x , y x,y x,y 的过程,叫扩展欧几里得算法
欧几里得算法的基本原理: g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a,b)=gcd(b,a\mod{b}) gcd(a,b)=gcd(b,amodb)
注: a m o d b = a − ⌊ a b ⌋ × b a\mod{b}=a-\lfloor\frac{a}{b}\rfloor\times b amodb=a−⌊ba⌋×b
Code
ll exgcd(ll a, ll b, ll &x, ll &y) {
//return gcd(a,b) and get x,y for ai*x+bi*y=gcd(ai,bi)
if(b == 0) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
使用扩展欧几里得求解线性同余方程
问题
求 x x x s . t . ( 使 得 ) s.t.(使得) s.t.(使得) a x ≡ b m o d m ax\equiv b\mod{m} ax≡bmodm
a x ≡ b m o d m ⇔ a x = m y + b ax\equiv b\mod{m} \Leftrightarrow ax=my+b ax≡bmodm⇔ax=my+b
⇔ a x − m y = b \Leftrightarrow ax-my=b ⇔ax−my=b
令 y = y ′ y=y' y=y′
⇔ a x − m y ′ = b \Leftrightarrow ax-my'=b ⇔ax−my′=b
使用扩展欧几里得 (成立条件: g c d ( a , m ) ∣ b gcd(a,m)|b gcd(a,m)∣b)求出 x x x
再把 x x x 放大 b / d b/d b/d 倍就得到了答案 d = g c d ( a , m ) d=gcd(a,m) d=gcd(a,m)
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) {//return gcd(a,b) and get x,y for ai*x+bi*y=gcd(ai,bi)。
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--) {
ll a, b, m;
scanf("%d%d%d", &a, &b, &m);
ll x, y;
ll d = exgcd(a, m, x, y);
if(b % d) puts("impossible");
//因为b是d的b/d倍,所以最终答案还要转化为在b意义下的答案,也就是把d扩大b/d倍
else printf("%lld\n", x * (b / d) % m);
}
return 0;
}
总结
求逆元的两种方式
- 如果模数是质数 ,快速幂 + 费马小定理
- 如果模数不是质数, e x g c d exgcd exgcd