写在前面
这篇博客是我在【数论】对 算术基本定理 的研究 中的一部分
- 拓展欧几里得算法
现在已经有了a-b == GCD(a,b) * (n1-n2),那就可以再来个直接点的
GCD(a,b) + (p*n1+q*n2) == GCD(a,b)
此处放到MOD群里面可以变形为求逆元素
即p*a+q*b == GCD(a,b)
想要求出一组p,q∈Z,满足上式
首先套用欧几里得算法的逻辑:
结合算术基本定理,有
GCD(a,b) == P1min(a1,b1)P2min(a2,b2)......Pnmin(an,bn)
a MOD b == P1a1-b1P2a2-b2......Pnan-bn(执行的条件为ai>=bi)
则能得到GCD(a,b) == GCD(b,a MOD b)
那么就有p*a+q*b == GCD(a,b) == GCD(b,a MOD b)
这样就可以写一个递归函数,一层一层MOD下去
这样化简,直到ai MOD bi == 0
即p*GCD(a,b) == GCD(a,b),那么在最后一层里面p==1
所以在拓展欧几里得里面,bi==0这一层中,ai即为GCD(a,b)
(到此为止和欧几里得算法一模一样,我甚至是复制的欧几里得算法)
而如果想要求出p和q,那么有一个回溯的过程就好了
如何回溯得到p和q呢?
来找一下相邻的层数间pi和pi+1,qi和qi+1的关系
因为有p*a+q*b == GCD(a,b)
则在每一层中:
GCD(a,b)
== pi*a+qi*b
== pi+1*b + qi+1*(a MOD b)
== pi+1*b + qi+1*(a - (a/b)*b)
== pi+1*b + qi+1*a - qi+1*(a/b)*b
== qi+1*a + pi+1*b - qi+1*(a/b)*b
== qi+1*a + (pi+1 - qi+1*(a/b))*b
即每层中pi == qi+1,qi == pi+1 - qi+1*(a/b)
然后就可以回溯了!
然后就能写出不返回gcd,单纯求出一组p,q的拓展欧几里得了!
C++:
1 #include<bits/stdc++.h> 2 #define OUT(x) cout<<#x<<":"<<x<<endl; 3 4 using namespace std; 5 6 int x,y; 7 8 void exgcd(int a,int b) 9 { 10 if(!b){x=1;y=0;} 11 else{ 12 exgcd(b,a%b); 13 int tmp=x; 14 x=y;y=tmp-(a/b)*y; 15 } 16 } 17 18 int main(int argc,char *argv[],char *enc[]) 19 { 20 exgcd(12,17); 21 OUT(x);OUT(y); 22 return 0; 23 }
上面这个版本是我写的,比较诡异,来个常见的版本
C++:
1 #include<bits/stdc++.h> 2 #define OUT(x) cout<<#x<<":"<<x<<endl; 3 4 using namespace std; 5 6 int x,y; 7 8 int exgcd(int a,int b) 9 { 10 if(!b){ 11 x=1;y=0; 12 return a; 13 } 14 int ret=exgcd(b,a%b); 15 int tmp=x; 16 x=y;y=tmp-(a/b)*y; 17 return ret; 18 } 19 20 int main(int argc,char *argv[],char *enc[]) 21 { 22 OUT(exgcd(12,17)); 23 OUT(x);OUT(y); 24 return 0; 25 }
仔细感受一下其中的差别啦~