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

  這兩個算法能夠說是OI裏數學模塊最重要的基礎了(若是位運算不算數學的話)。html

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

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

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

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

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

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

 

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

 

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

  由於代碼很短,因此算法的過程我就不贅述了,主要就是遞歸,能夠從代碼中看出來。htm

  若是趕時間(好比距離noip就差一天了),能夠忽略掉證實只記代碼,可是我認爲證實仍是必要的,證實在這裏:證實blog

 

二.擴展歐幾里得算法

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

    貝祖定理:若存在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;
}
相關文章
相關標籤/搜索