关于Euclid算法
Euclid算法大概是我最早接触的东西了吧,下面是学长传授的代码:
1 int GCD(int a,int b){ 2 return b==0?a:GCD(b,a%b); 3 }
短小精悍。当时也没理解为什么这段代码可以求出a和b的最大公因数。现补下证明。
a和b的最大公因数记为gcd(a,b),简写为(a,b).
证明Euclid算法的正确性,即证明(a,b)=(a,b−ka).
设x=(a,b),y=(a,b−ka),
∵,
\therefore x|a,x|b,
\therefore x|(-ka+b),
\therefore x|(a,b-ka)=y,
故x \leqslant y.
\because y=(a,b-ka),
\therefore y|a,y|(b-ka),
\therefore y|[ka+(b-ka)]=b,
\therefore y|(a,b),即y|x,
故y \leqslant x.
\therefore x=y.
即(a,b)=(a,b-ka).证毕.
当k=\lfloor \frac{a}{b} \rfloor时,即为Euclid算法.
上述Euclid算法仅求出(a,b),而不能得到(a,b)关于a和b的线性表示,故有了拓展的Euclid算法:
1 int EXGCD(int a,int b,int &x,int &y){ 2 if(b==0){ 3 x=1;y=0; 4 return a; 5 } 6 int d=EXGCD(b,a%b,x,y); 7 int t=x; 8 x=y;y=t-a/b*y; 9 return d; 10 }
上述算法可求得x和y,使满足(a,b)=ax+by.
为便于证明,以非递归版本为例:
1 int EXGCD(int a,int b,int &x,int &y){ 2 int x0=1,x1=0,x2=a; 3 int y0=0,y1=1,y2=b; 4 while(y2!=0){ 5 int q=x2/y2; 6 int t0=x0,t1=x1,t2=x2; 7 x0=y0;x1=y1;x2=y2; 8 y0=t0-q*y0;y1=t1-q*y1;y2=t2-q*y2; 9 } 10 x=x0;y=x1; 11 return x2; 12 }
若ax_0+bx_1=x_2,ay_0+by_1=y_2,
不难得到a(x_0-qy_0)+b(x_1-qy_1)=x_2-qy_2.
上述等式正式保证了拓展Euclid算法的正确性。
拓展Euclid算法可用来求模线性方程ax+by=(a,b)的解。
特别地,当(a,b)=1时,x即为a在b下的乘法逆元。
求1到n的数在p下的乘法逆元可以做到O(n)的复杂度:
令inv_i为i在p下的逆元,则有inv_x \equiv [(p- \lfloor \frac{p}{x} \rfloor ) \times inv_{p\%x}](mod p).
证明:设p=kx+r,其中0 \leqslant r < x,
那么原式等于inv_x \equiv [(p-k) \times inv_r](mod p)
\Leftarrow r \equiv [(p-k) \times x](mod p)
\Leftarrow r \equiv -kx(mod p)
即kx+r \equiv p \equiv 0(mod p),证毕.
故我们可以递推得到各个逆元。