歐幾里得算法和擴展歐幾里得算法 數論基礎


  這兩個算法可以說是OI里數學模塊最重要的基礎了(如果位運算不算數學的話)。

一.歐幾里得算法(Euclidean Algorithm)

  模板水題:LOJ P1212  (LOJ真是個好東西啊)

  在學習一種算法前,我認為我們首先應該知道,這種算法是要解決什么問題的。

  小學就已經學過了兩個數的最大公約數,而歐幾里得算法就是為了求出兩個數a、b的最大公約數的,這個最大公約數可以表示為gcd(a,b)。

  歐幾里得算法又稱輾轉相除法,這個名字已經揭示了它的主要思想:輾轉相除!

  它的函數代碼只有一行,簡單便捷,復雜度O(log n):

 

    int gcd(int a,int b){ return b?gcd(b,a%b):a; }

 

  不要小看這短短的一行代碼,其中蘊含了無盡的人生智慧(/滑稽)

  因為代碼很短,所以算法的過程我就不贅述了,主要就是遞歸,可以從代碼中看出來。

  如果趕時間(比如距離noip就差一天了),可以忽略掉證明只記代碼,但是我認為證明還是必要的,證明在這里:證明

 

二.擴展歐幾里得算法

  首先我們要理解一個定理:

    貝祖定理:若存在a、b是整數,則必存在整數x、y,滿足ax+by=gcd(a,b)。

  證明在這里寫得比較清楚:證明

  需要耐心理解。

  貝祖定理

  證明:

    當 b=0 時,gcd(a,b)=a,此時 x=1 , y=0

    當 b!=0 時,

      設 ax1+by1=gcd(a,b)=gcd(b,a%b)=bx2+(a%b)y2

      又因 a%b=a-a/b*b

      則 ax1+by1=bx2+(a-a/b*b)y2

          ax1+by1=bx2+ay2-a/b*by2

           =ay2+bx2-b*a/b*y2

           =ay2+b(x2-a/b*y2)

      解得 x1=y2 , y1=x2-a/b*y2

    因為當 b=0 時存在 x , y 為最后一組解

    而每一組的解可根據后一組得到

    所以第一組的解 x , y 必然存在

    證畢。

  顯然,因為當b=0時,x=1,y=0,這時x和y是已知的,所以我們很容易想到通過遞歸來求解。

  不斷返回下一層的解,來得到這一層的解,最終回溯回來,得解。

  因為是借助歐幾里得算法進行回溯的,所以復雜度也是O(log n)。

  基本理解擴展歐幾里得算法后,我們就可以來看看例題了。

 

  例題:洛谷oj P1082

  同余定理:給定一個正整數$m$,如果兩個整數$a$和$b$滿足$a-b$能夠被$m$整除,即$(a-b)/m$得到一個整數,那么就稱整數$a$與$b$對模$m$同余,記作$a\ ≡\ b\ (\ mod \ m \ )$。對模$m$同余是整數的一個等價關系。

  其實就是$a\ mod\ m\ =\ b\ mod\ m$。

  當$b>=1$時,因為$1\ mod\ b\ =\ 1$,所以$ax\ ≡\ 1(mod b)$就是$ax mod b=1$。其實就差不多是方程$ax\ +by\ =\ 1$,y可能為負數,所以我們在做exgcd后還要加個答案處理。

  ac代碼:

#include <cstdio>
using namespace std;

long long a,b;

long long x,y;
inline void exgcd(long long a,long long b){
    if (b==0){
        x=1,y=0;
        return ;
    }
    else exgcd(b,a%b);
    long long x1=x;
    x=y,y=x1-a/b*y;
    return ;
}

int main(){
    scanf("%lld%lld",&a,&b);
    exgcd(a,b);
    while (x<0)
        x+=b;
    x%=b;
    printf("%lld",x);
    return 0;
}

 

 

 

 

 

 

 

 


免責聲明!

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



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