原理推导:
首先我们定义:设a和b不全为0,则存在整数x和y,使得gcd(a,b)=ax+by。这就是扩展欧几里得的算法描述。
扩展欧几里得算法是用来求解二元一次方程。对于方程Ax+By=C,其中A,B,C为整数,求出满足等式的整数x和y。考虑到给出的方程不一定是最简形式,所以我们需要先化为最简式,设d=gcd(A,B),那么化简后的方程为:ax+by=c,其中a=A/d,b=B/d,c=C/d;
此时,gcd(a,b)=1,先将方程ax+by=gcd(a,b)=1解出后,设x0,y0为方程的特解,那么原方程的最终解为c*x0,c*y0.现在问题来了,怎么求方程ax+by=1的解呢?这就是我们要讨论的扩展欧几里得了。
扩展欧几里得是求方程ax+by=gcd(a,b)的解。设a>b,当b=0时,gcd(a,b)=a,此时x=1,y=0;否则我们设:
a*x1+b*y1=gcd(a,b);
b*x2+(a mod b)*y2=gcd(b,a mod b);
在欧几里得算法中,我们有gcd(a,b)=gcd(b.a mod b),所以我们进一步得到:
a*x1+b*y1 = b*x2+(a mod b)*y2 = b*x2+(a-|_ a/b _|*b)*y2 = a*y2+b*(x2-|_ a/b _|*y2);
所以有:x1=y2; y1=x2-|_ a/b _|*y2;
举个例子来说吧,考虑二元一次方程48x+30y=gcd(48,30),我们先通过欧几里得递归求出它们的最大公约数6,然后逐层回溯得到x和y:
递归过程:48=1×30+18
30=1×18+12
18=1×12+6
12=2×6+0
回溯过程:6=18-(1×12)
=18-1×(30-18×1)
=18-1×(30-(48-30×1)×1)
=48×2-30×3
递归实现代码如下:
void exgcd(LL a,LL b,LL &x,LL &y) { if(b==0) { x=1; y=0; return; } exgcd(b,a%b,x,y); LL t=x; x=y; y=t-a/b*y; }
非递归实现代码如下:
typedef long long LL; LL exgcd(LL m,LL &x,LL n,LL &y) { LL x1,y1,x0,y0; x0=1;x1=0; y0=0;y1=1; LL r=(m%n+n)%n; LL t=(m-r)/n; x=0;y=1; while(r) { x=x0-t*x1; y=y0-t*y1; x0=x1;y0=y1; x1=x;y1=y; m=n;n=r;r=m%n; t=(m-r)/n; } return n; }
应用与分析:
上面讲了扩展欧几里得算法的原理,通过这个方法可以得到二元一次方程的特解,实际上,得到特解之后,就可以得到通解,如下:
x=c*x0-b*t;
y=c*y0+a*t;
其中t∈Z,将上述解代入方程,有:
ax+by=a*(c*x0-b*t)+b*(c*y0+a*t)=a*c*x0+b*c*y0=(a*x0+b*y0)*c=c;
在ACM中,扩展欧几里得的应用主要包括:
(1)求解不定方程;
(2)求解模的逆元;
(3)求解同余方程;
例题:
POJ1061µhttp://poj.org/problem?id=1061
http://acm.hit.edu.cn/hoj/problem/view?id=2815
http://poj.org/problem?id=2142
http://codeforces.com/problemset/problem/7/C