1. 程式人生 > >【數論】拓展歐幾里得演算法

【數論】拓展歐幾里得演算法

寫在前面

  這篇部落格是我在【數論】對 算術基本定理 的研究 中的一部分

  • 拓展歐幾里得演算法

   現在已經有了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+1qi == 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 }

 

  仔細感受一下其中的差別啦~