擴展歐幾里德算法(遞歸及非遞歸實現c++版)


今天終於弄懂了擴展歐幾里德算法,有了自己的理解,覺得很神奇,就想着寫一篇博客。

 

在介紹擴展歐幾里德算法之前,我們先來回顧一下歐幾里德算法。

歐幾里德算法(輾轉相除法):

  輾轉相除法求最大公約數,高中就學了,但當時知其然不知其所以然,直到大學才真正理解它的精髓。

  理解輾轉相除,關鍵在於理解 gcd(a,b)==gcd(b,a%b)

  那么怎么去理解呢?下面是我的理解:

    首先對於非負整數a,b,一定可以寫成 a=k*b+r(r<b) 的形式

    令 g=gcd(a,b) ,則有 g|a ,即 g|(k*b+r) 

            又 g|b ,所以 g|r

    當然,只證得 g 同時整除 b 和 r 依然不能得出 g==gcd(b,r),還可能是gcd(b,r)>g 。

    於是我們假設存在 g‘>g(故g'不可能是 a,b 的公約數)

            使得 g'|b 且 g'|r

            則有 g'|(k*b+r) 即 g'|a

           → g' 是 a,b 的公約數,矛盾。

    故 gcd(a,b)==gcd(b,r)(其中 r==a%b) 得證。

 

  下面貼出歐幾里德算法的遞歸和非遞歸代碼:

1 //遞歸歐幾里德算法
2 int gcd(int a,int b)
3 {
4     return b==0?a:gcd(b,a%b);
5 }
 1 //非遞歸歐幾里德算法
 2 int gcd(int a,int b)
 3 {
 4     int t;
 5     while(b){
 6         t=b;
 7         b=a%b;
 8         a=t;
 9     }
10     return a;
11 }

  值得注意的是:

    1、任意正整數和 0 的最大公約數為正整數本身。

    2、即使參數 a<b ,經過一次遞歸或循環之后,a,b 會自動交換,故開頭不需要加 if(a<b)swap(a,b); 語句。

 

擴展歐幾里德算法:

  擴展歐幾里德算法其實就是為了求出 ax+by=gcd(a,b) 的一個整數解 (x0,y0) ,然后就能得出全部整數解為 (x0+k*b,y0-k*a) 。

  遞歸實現

     擴展歐幾里德算法的遞歸實現是基於遞歸求gcd的過程,它會在gcd函數遞歸完返回的時候逐步解出一個整數解(x,y) 。

     讓我們來簡單模擬一下 gcd(a,b) 遞歸返回的過程(數學歸納法得出遞推公式)

       ①在gcd遞歸到最深處(倒是第一層)的時候,即此時 b0==0 ,可得出方程 a0x+b0y=gcd(a,b) (gcd(a,b)==gcd(a0,b0)==gcd(a1,b1)==…==a0)的一組整數解為x0=1,y0=0。

       ②假設返回到倒數第 k 層時方程 akx+bky=gcd(a,b) 的一組整數解為 xk , yk 

        又設返回到倒數第 k+1 層時方程 ak+1x+bk+1y=gcd(a,b) 的一組整數解為 xk+1 , yk+1 

        則有 akxk+bkyk=ak+1xk+1+bk+1yk+1 其中 ak==bk+1 , bk=ak+1%bk+1 

        不妨令 xk+1=yk   yk+1=xk-ak+1/bk+1*yk 

     下面貼出遞歸代碼:

 1 //遞歸擴展歐幾里得算法
 2 int exgcd(int a,int b,int&x,int&y)
 3 {
 4     if(b==0){
 5         x=1,y=0;
 6         return a;
 7     }
 8     int r=exgcd(b,a%b,y,x);
 9     y-=a/b*x;
10     return r;
11 }

  非遞歸實現

    個人感覺擴展歐幾里德算法的非遞歸方式理解起來更為簡單。

    我們就從一個具體的例子入手吧。

        若 a=30 , b=47 , 則 gcd(a , b)=1,那么就是要求解一次不定方程 30x+47y=1 的整數解。

        由已知,我們有

                     

 

           觀察上述矩陣,第三列的元素變換過程恰好是輾轉相除法求最大公約數的過程。

        他們的每一步的變換形式(行變換)可用如下通式表示:

               

 

        當第 2 行 3 列的元素為 0 時,第 1 行 3 列的元素即為 gcd(a , b)。

        故最終能得出一組整數解 (x , y)=(11 , -7) 滿足 30x+47y=1 。

        於是我們可以在非遞歸歐幾里德算法的基礎上寫出非遞歸的擴展歐幾里德算法,代碼如下:

 1 //非遞歸擴展歐幾里得算法
 2 int exgcd(int a,int b,int&x,int&y)
 3 {
 4     int m=0,n=1,t;
 5     x=1,y=0;
 6     while(b){
 7         t=m,m=x-a/b*m,x=t;
 8         t=n,n=y-a/b*n,y=t;
 9         t=b,b=a%b,a=t;
10     }
11     return a;
12 }

 

寫到這里算是終於寫完了。

這是我寫的第一篇博客,本來旨在用最簡潔的語言描述最核心的思想,但貌似……可能……大概……與預期有點不符...

我希望通過寫博客記錄一下自己的想法,即使日后忘記也能一看便知。當然,如果能幫助別人深入理解,那就再好不過了。

最后,如果各位發現文中的錯誤、有什么建議或者有一些自己的想法,都歡迎在評論區提出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM