歐幾里得+擴展歐幾里得(理解)


歐幾里得:


輾轉相除法
代碼:

typedef long long LL;  

LL gcd(LL a, LL b) {
    return b == 0 ? a : gcd(b, a%b);
}

用途:是一個求最大公約數的計算方法;

自己的理解:對於傳入的數值進行判定,是否b位置爲0(因爲b隨着調用會改變),如果不爲0繼續調用,a,b的值進行變換,
如果爲0,返回a位置的值;

看起來與最大公約數無關,其實不然,首先想一想什麼是最大公約數,是一個能被這a,b兩個數整除的一個數,一定比a,b都小,假設a%b==0,那麼存在一個x,使得a=bx,我們不管x的值爲多少,但是a對b取餘,和b對b取餘,都爲0,那麼b就是它的一個公約數,現在不能保證一定是最大公約數。

如果我們從ab之間最小的那個開始判定(先假設a%b==0),假如a>b,那麼b一定是a,b的最大公約數,例如:8和4,4爲8,4的最大公約數。

我們得到一個結論:如果a%b==0,那麼b爲a,b的公約數。

我們去掉這個假設a%b!=0呢,我們知道,如果a%b!=0(a>b),a中包含b所沒有的素數(每個正整數都可以唯一表示成素數的乘積),利用這個思想,我們把a中b沒包含的素數剔除(a%b),這樣一來,b一定比a%b大,但是這也不能一定說明,b%(a%b)爲0,還是需要判定。

然後繼續調用函數,把a,b的值重新定義,現在的a,b已經不是上一次的a,b了,叫做a1b1 ,繼續做a1%b1的判定,直到ax %bx ==0,現在的bx 一定是最大公約數,因爲bx 是從大到小開始判定的。

有一個問題出來了,ax %bx 一定會有等於0的時候嗎,答案是一定的,因爲1是所有數的因數,兩個素數的最大公約數就是1,一次次的取餘,互換位置,最終的結果就是ax =1,bx =1,然後取餘爲0,返回了a的值 1。


擴展歐幾里得:


代碼:

typedef long long LL;  

LL extgcd(LL a, LL b, LL &x, LL &y) {
    LL d = a;
    if(b != 0) {
        d = extgcd(b, a%b, y, x);
        y -= (a / b) * x;
    } else {
        x = 1; y = 0;
    }
    return d;
}

用途:對於二元一次方程組的求解:ax+by=1;
自己的理解:先思考一個問題,如果ax+by=1成立,那麼a,b的最大公約數爲1,也就是說gcd(a,b)==1,說明當gcd(a,b)!=1,a,b不互質,方乘無解。
從代碼上看,其中有gcd的東西,不一樣的是,這個需要一直算到b=0,纔會停止,然後執行 y -= (a / b) * x,這裏的過程類似於遞歸,每次a,b交換,x,y跟着交換,最後求答案的時候,逆着過程來一遍,就可以得到答案了。
**例子:**6x+4y=1(6,4,x,y)

6x+4y=164,x,y)
d = 6;
b != 0; 
d = 4y + 2x;(42,y,x)//調用函數,{}裏都是函數執行的內容,注意這裏是y,x
{
    d = 4;             
    b != 0;             //if裏的判定
    d = 2x(20,x,y)  //調用函數,{}裏都是函數執行的內容
    {
        d = 2;
        b == 0;         //執行了else的內容
        x = 1, y = 0;
        return d=2;     //返回
    }
    y = y - (a / b) * x = 1 - 2 * 0 = 1;//這裏爲什麼y=1呢,問題1(見下分析)
    return d=2;
}
y = y - (a / b) * x = 0 - 1 * 1 = -1;   //這裏纔是對上的x,y的值
return d=2;             //d在函數一開始的時候定義了,第一次返回是多少就是多少

問題1:爲什麼我要x=1,y=0,而調用的時候是y=1呢,仔細觀察源代碼,在調用的時候d = extgcd(b, a%b, y, x);,這裏x,y的位置互換了,加了&,各自是各自對應的值,每次調用都會互換位置,其中很複雜,必須模擬程序的時候仔細分析才行,不管怎麼樣,代碼就在那裏,這裏不清楚也沒關係。

順便說一下返回的d就是歐幾里得最大公約數。

說完這個例子是不是更好理解這個歐幾里得了呢。


然後推廣一下,ax+by=c的情況

如果變成ax+by=c的話,解的組數會有很多個,一般題目會要求輸出一個數對於另一個數的乘法逆元之類的。

什麼是乘法逆元?
a*x ≡ 1(mod m)
意思就是 a*x % m == 1,它的意思大致就是使x的值變小,但是要非負。

考慮這個問題之前,需要知道,什麼時候無解,△?太麻煩了。如果數字很大,就會爆數據。

前面判定有沒有解是gcd(a,b)!=1, 的時候無解,也就是說gcd(a,b)==1的時候有解,現在把1換成了c,gcd(a,b)==c?肯定不可以,gcd返回的是一個約數,而且是最大的,c完全也是其中一個小一點的約數。

聰明的你也許已經看出來了,只要abc的約數一致就可以了,令d=gcd(a,b)
判定gcd(d,c)==0,更簡單的表達

c % gcd(a,b) == 0

反之無解。

話不多說先貼代碼

typedef long long LL;  

LL extgcd(LL a, LL b, LL &x, LL &y) {
    if(!b) {
        x = 1;
        y = 0;
        return a;   
    }
    LL ans = extgcd(b, a%b, x, y);
    LL t = x;
    x = y;
    y = t - a / b * y;
    return ans;
}

LL cal(LL a, LL b, LL c) {
    LL x, y;
    LL d = extgcd(a, b, x, y);
    if(c % d != 0) return -1;
    x = x * c / d;          //①
    b /= d;                 //②
    if(b < 0) b = -b;       
    LL ans = x % b;         //③
    if(ans <= 0) ans += b;
    return ans;
}

①:c是結果,d是a,b的最大公約數,c可能是比d小的公約數,所以c/d是擴大/縮小的倍數,x乘上,就是與c一致的倍數,也就是ax+by==c成立的時候,x的最大值。
②:b中有多少個d
③:除去x中新b的部分

爲什麼要這樣做呢
因爲知道了x ,y也就知道了,所以這裏只對x操作。
把x中b可以替換的部分拿去b,減少x的值,變相的增加了y的值,在成立的基礎上,減小x,增大y。

得到的ans就是x的值,y利用已知就可以得到了。

發佈了155 篇原創文章 · 獲贊 16 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章