引理
- 如果任意整数a、b不都为0,则gcd(a,b)是集合A={ax+by:x,y∈Z}的最小正元素。
证明:
- 设s是A中的最小正元素,也就是s=ax′+by′。
- 设q=⌊a / s⌋,则
a%s=a−qs=a−q(ax+by)=a(1−qx)+b(−qy)
- 因此,a%s∈A。由于0≤a % s<s,所以a%s=0。同理,b%s=0。因此,s | gcd(a,b),即gcd(a,b)>=s
同时,因为s∈A,所以gcd(a,b)|s,也就是说gcd(a,b)≤s。得到gcd(a, b)=s。
扩展欧几里得算法.
扩展欧几里得算法(扩O)能在求gcd(a,b)的同时求出丢番图方程ax+by=gcd(a,b)的解。
然而怎么求呢?我们观察gcd(a,b)=gcd(b,a%b),所以设如下两个方程:
ax+by=gcd(a,b)=d
bx′+(a%b)y′=gcd(b,a%b)
明显gcd(a,b)=gcd(b,a%b),也就是ax+by=bx′+(a%b)y′。
为了求得x与y,我们需要保证a,b不变,所以:ax+by=bx′+(a%b)y′=bx′+(a−[a/b]∗b)y′
=ay′+b(x′–[a/b]y′) ([a/b]表示a/b的值向下取整。)
所以可得恒等关系:x = y’ , y = (x’ – [a/b]y’)。一层层往上推,这样就求出了一组特解。
那么如何求全解呢?直接给出递推式:
x=x0+(b/gcd)∗t
y=y0–(a/gcd)∗t
明显,b/gcd与a/gcd是互素的,也就是说它们的gcd为1。如果它们再除以一个值,某些解可能就不是整数了。
裴蜀定理
扩O有什么卵用吗?其实它可以求线性丢番图方程(不定方程)。
对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程(称为裴蜀等式):
ax+by=m
有解当且仅当m是d的倍数。这是由于任何一个a和b的线性组合都被gcd(a, b)整除。
裴蜀等式有解时必然有无穷多个整数解,每组解x、y都称为裴蜀数,可用扩O求得。只要将x与y同时乘上 m / gcd(a,b)就行了。
特别来说,方程 ax+by=1有解当且仅当整数a和b互素。(因为 gcd(a,b)=1)
乘法逆元
给定一个形如 ax ≡ 1(%m)的关于x的方程,x即为a关于m的乘法逆元。
怎么求呢?我们把它等价一下:ax%m=1%m=1,ax=m(−y)+1,ax+my=1。
也就是说,只要求出ax+my=1这个方程的解就行了。是不是有点熟悉?对,用扩O+裴蜀求出x,y。
(注意要满足 gcd(a,m)=1)
但是明显,一道题目出给你不可能让你输出无数解,通解也不现实,一般是让你求出x最小的正整数解。那怎么办呢?
x的通解是x0+m/gcd∗t,在乘法逆元情况下gcd=1,所以不用考虑。所以x的通解是x0+mt。
因为通解是x0+mt,明显最小正整数解的区间是,)[0,m),所以似乎只要让x0 % m就可以找到最小解了。
可是万一x0 % m是负数呢?答案x=(x0%b+b)%b。
因为如果x0为负,答案就是b减去x0 % b的绝对值。如果x为正,还要除以b。
说了这么多,理一下思路:扩O能求出ax+by=gcd(a,b)。那么只要整数m=整数∗gcd(a,b),方程就有解,这种方程叫做不定方程。同时根据乘法逆元的性质,只当gcd(a,b)=1时a,b互为乘法逆元。
下面贴代码:
Luogu P1082 同余方程
求关于 x 的同余方程 ax ≡ 1 (% b)的最小正整数解。
#include <cstdio>
using namespace std;
int a, b;
void exgcd(int a, int b, int &x, int &y){
if (b){
exgcd(b, a%b, x, y); int tmp=x;
x=y; y=tmp-(a/b)*y;
} else x=1, y=0;
return;
}
int main(){
scanf("%d%d", &a, &b);
int x, y;
exgcd(a, b, x, y);
x=(x%b+b)%b;
printf("%d", x);
return 0;
}