人类为什么需要扩欧?
扩展欧几里得这玩意儿吧,原本上来说应该是求方程 ax+by=gcd(a,b) 的解的。但是考虑到计算机中常用的逆元,它可以表示为 ax≡1(mod b) ,那么这个式子可以变形为 ax−by=1 。
然后呢,在自然数中,只有互质的两个数才会有逆元。这个问题也比较容易证明,设存在两个不互质的正整数
a,b
满足
ax≡1(mod b)
,那么设这两个数的最大公约数为
g(g≠1)
,那么
a=p∗g
,
b=q∗g
。于是原式可以表示为
p∗g∗x−q∗g∗y=1
,提一个公因式
g
就变成了
好吧回到原来的话题。
ax−by=1
(其实写成
ax+by=1
也无所谓对吧反正这个y在这里没啥用好像)可以再进一步写成
ax+by=gcd(a,b)
。然后这就到了扩欧能解决的范围内了!
求逆元基本上是扩欧最常见的用途啦。
扩欧为什么能求 ax+by=gcd(a,b) 的解?
扩展欧几里得之所以得名,就是因为它跟平常求gcd的欧几里得算法有密不可分的关系。在欧几里得算法中我们通过把 gcd(a,b) 转化成 gcd(b,a%b) ,实现一步步递归求解;在扩展欧几里得中,我们同样可以做这样的变形。
既然有式子 ax+by=gcd(a,b) ,那么同样就有式子 bx′+(a%b)y′=gcd(b,a%b) 。因为 gcd(a,b)=gcd(b,a%b) ,那么如果我们知道了第二个式子的解,就可以通过变形得到第一个式子的解。
首先考察递归终止情况:
b=0
时,
a
就是原来调用的
那么当我们知道 bx′+(a%b)y′=gcd(b,a%b) 的解的时候,我们可以对式子作出如下变形:( 为了方便,这里把 gcd(a,b) 直接用 g 代替)。
- 把
a%b 变成 a−a/b∗b (这里的除法是整除),然后把式子展开变成 bx′+ay′−(a/b∗b)y′=g 。- 进一步整理变成 ay′−b∗(x′−a/b∗y′)=g 。这样我们可以看出来这个式子和前面要求的式子 ax+by=g 的形式是一样的,那么我们就求出来了原来的式子的一组解: x=y′,y=x′−a/b∗y′ 。注意是一组解。这种不定方程的解有很多,我们只是求出来了一组。
这样每次把 gcd(a,b) 换成 gcd(b,a%b) 递归下去就可以出解了。这里贴一个板子题:CodeVS1200 同余方程。注意这个题要求最小正整数解,而求出来的x可能为负数,所以要把它模成正的。
这就是扩展欧几里得求逆元的一个标准模式啦!
#include<cstdio> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int a,b,x,y; int exEuclid(int a,int b) { if (b==0) { x=1;y=0;return a; } int t,ans=exEuclid(b,a%b); t=x; x=y; y=t-(a/b)*y; return ans; } int cal(int a,int b) { int ans,g=exEuclid(a,b); b=abs(b); ans=x%b; if (ans<=0) ans+=b; return ans; } int main() { scanf("%d%d",&a,&b); printf("%d",cal(a,b)); return 0; }
那我还想要更多的解怎么办?
对于方程 ax+by=gcd(a,b) 来说,用扩展欧几里得求出的一组解是满足 |x|+|y| 最小的(这个东西愚蠢的ATP也不会证明啦。。是看了TA学长的博客才知道的。。学长有一大——坨证明,ATP看着就头大。。贴下学长的blog在这里啦)。
然后如果一直把 x 加上b/g , y 减去a/g ,那么得到的仍然是一组满足条件的解。正确性把 x′=x+b/g,y′=y−a/g 代进去就会发现方程仍然是成立的啦。扩欧会出现无解情况吗?
如果你是要求 ax+by=gcd(a,b) 的话那肯定有解的啦。。好像有个什么贝祖定理啥的是讲这个事的啦。。
但是如果你要求 ax+by=c ,随便扔一个c过去的话肯定没那么简单啦。。首先扩欧必须要求的是 ax+by=gcd(a,b) ,那也就是说当 gcd(a,b) 不是1的时候这个式子两边肯定可以约掉一个 gcd(a,b) 。那如果发现 c 不能整除gcd(a,b) 就无解了对吧。。
但是就算约掉了一个 gcd(a,b) ,它也不一定能化成标准的 ax+by=1 的形式,可能是 ax+by=c′ 。这也没什么关系,求出 ax+by=1 的解以后乘上c’就好了!仍然是贴两道题啦
- BZOJ1477 青蛙的约会
照着题意稍微推一下式子就有了扩欧的方程啦。注意判断无解。 - BZOJ1407 Savage
也是裸的扩欧题目,不过各种对于正负数啊啥的判断好像比上一个题麻烦一点。
(还真是只有两道题= =)
最后再提一句
扩展欧几里得还有一个非常简短的形式啦!是出自fye学姐的板子!
这里就贴一下zyf2000的博客里的一个链接啦。- BZOJ1477 青蛙的约会