1、扩展欧几里得算法
已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足等式
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)
ps:这个gcd(a,b)是a和b能凑出来的最小的正整数
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1,y = 0;
return a;
}
//注意这里调换了一下x,y,这样可以在下面只变化y,不用变x
int d = exgcd(b,a % b,y,x);
y -= a / b * x;
return d;
}
当b == 0 的时候,gcd(a,b) == a,所以只需要令x == 1,y == 0即可。
当b != 0 的时候,我们继续递归得到d的时候,我们已经有了
by + (a % b)x = d,此时我们希望通过这个式子得到我们当前的ax+by的凑法,只需要将上式进行等价变形:
b
y
+
(
a
%
b
)
x
=
b
y
+
(
a
−
a
/
b
∗
b
)
x
=
a
x
+
b
(
y
−
a
/
b
∗
x
)
by + (a \% b)x = by + (a - a / b * b)x = ax + b(y - a / b * x)
by+(a%b)x=by+(a−a/b∗b)x=ax+b(y−a/b∗x)
由此得到x = x,y = (y - a / b * x)
2、线性同余方程
利用扩展欧几里得方法可以轻松解决线性同余方程。
有如下的线性同余方程
a
x
≡
b
(
m
o
d
m
)
ax \equiv b(mod\ m)
ax≡b(mod m)
这等价于存在整数y使得
a
x
=
m
y
+
b
ax = my + b
ax=my+b,也即
a
x
−
m
y
=
b
ax - my = b
ax−my=b
此方程有解当且仅当gcd(a,m) | b
因为a是gcd(a,m)的倍数,m也是gcd(a,m)的倍数,所以他们两个的线性组合也是gcd(a,m)的倍数。如果b不是,那么就没有解
1. 例题线性同余方程
给定 n 组数据 ai,bi,mi,对于每组数求出一个 xi,使其满足 ai×xi≡bi(modmi),如果无解则输出 impossible。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一组数据 ai,bi,mi。
输出格式
输出共 n 行,每组数据输出一个整数表示一个满足条件的 xi,如果无解则输出 impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int 范围之内。
数据范围
1≤n≤105,
1≤ai,bi,mi≤2×109
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
-3
#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,m,x,y;
cin >> a >> b >> m;
int d = exgcd(a,m,x,y);
if(b % d == 0)
{
x = b / d * (long long)x % m;
cout << x << endl;
}else{
cout << "impossible" << endl;
}
}
}
只需要解释一下,最后将x扩大了 b/d 倍,再对m取模之后,才是最后的结果
3、使用扩展欧几里得方法求逆元
a有逆元的充要条件是a与p互质,所以gcd(a, p) = 1。假设a的逆元为x,那么有a * x ≡ 1 (mod p)
等价:ax + py = 1。如果d= exgcd(a, p, x, y) == 1那么二者之间存在乘法逆元,否则二者有公因子,不存在乘法逆元。
int d = exgcd(a, p, x, y);
if (d == 1) cout << ((LL)x + p) % p << endl;//保证x是正数
else puts("impossible");
参考资料
https://www.acwing.com/solution/content/3054/