温馨提示:本文推式子比较多,建议跟着文章自己推一推。
扩展欧几里得是什么
扩展欧几里得(exgcd)是一个可以用来求 ax+by=c(c%gcd(a,b)=0,否则无解)的解的算法
求解 ax+by=gcd(a,b)
首先,如果 b=0 的话,gcd(a,b)=a,则解为 {x=1y=0
设此方程的解为 {x=x0y=y0
那么我们需要做的就是将 ax0+by0=gcd(a,b) 转化为 b=0 的格式,这就要用到辗转相除法了。
设另一个方程:bx1+(a%b)y1=gcd(b,a%b)
令 a1=b,b1=a%b
则该方程转化为 a1x1+b1y1=gcd(a1,b1)
我们会发现它和原方程的格式是一样的,而且根据欧几里得原理,它可以一直递推到 anxn+bnyn=gcd(an,bn) 使得 bn=0,就可以求得解 {xn=1yn=0
那假设我们已经求得了该结果,那如何推导出 x0 和 y0 呢?
我们首先研究如何从 x1 和 y1 推导出一组合法的 x0 和 y0,其他的就同理了
因为
{bx1+(a%b)y1=gcd(b,a%b)ax0+by0=gcd(a,b)
且根据欧几里得定理,gcd(a,b)=gcd(b,a%b)
所以
ax0+by0=bx1+(a%b)y1
且 a%b=a−⌊ba⌋b(模运算的意义)
所以
ax0+by0=bx1+(a−⌊ba⌋b)y1=bx1+ay1−⌊ba⌋by1=b(x1−⌊ba⌋y1)+ay1=ay1+b(x1−⌊ba⌋y1){x0=y1y0=x1−⌊ba⌋y1
这样,我们就由 x1 和 y1 推导出了 x0 和 y0,其他同理
于是乎:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct node { int x,y; }; node exgcd(int a,int b) { if(b==0) { node tmp; tmp.x=1,tmp.y=0; return tmp; } node tmp=exgcd(b,a%b); node ans; ans.x=tmp.y,ans.y=(tmp.x)-a/b*(tmp.y); return ans; }
|
这样,我们就求出了 ax+by=gcd(a,b) 的一组解
ax+by=c 的一组解 {x=xtmpy=ytmp
我们已经求出了 ax+by=gcd(a,b) 的一组解 {x=x0y=y0
那么我们就可以知道 akx0+bky0=kgcd(a,b)
又因为要求 c 是 gcd(a,b) 的倍数(否则无解)
所以 k=gcd(a,b)c
所以很简单:
{xtmp=kx0=gcd(a,b)cx0ytmp=ky0=gcd(a,b)cy0
3.ax+by=c 的所有解 {x=xansy=yans
我们已经求出了 ax+by=c 的一组解 {x=xtmpy=ytmp
即 axtmp+bytmp=c
将它加上再减去 gcd(a,b)ab,得到
axtmp+gcd(a,b)ab+bytmp−gcd(a,b)ab=ca(xtmp+gcd(a,b)b)+b(ytmp−gcd(a,b)a)=c
在 xtmp 上减,在 ytmp 上加也同理
所以 {x=xtmp±gcd(a,b)by=ytmp∓gcd(a,b)a 也是一组解
这个变换进行多次,即可得到
{xans=xtmp+t×gcd(a,b)byans=ytmp−t×gcd(a,b)a
x 和 y 各自的最小正整数解
以 x 的最小正整数解为例:
求出任意一组解 {x=xtmpy=ytmp
因为将 xtmp 加或减 gcd(a,b)b 也成立,所以可设 d=gcd(a,b)b(注意这里分子是 b )
xmin=(xtmp%d+d)%d(因为 xtmp 有可能是负数)
同理对于 y,设 d=gcd(a,b)a(注意这里分子是 a)
ymin=(ytmp%d+d)%d
完结撒花
至此,你已经学完了扩展欧几里得的基础用法,如有不懂的地方,建议对照着文章自己推一推,悟一悟。
做个题练习一下吧:洛谷 P5656 【模板】二元一次不定方程 (exgcd)