歐幾里得:
輾轉相除法
代碼:
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了,叫做
有一個問題出來了,
擴展歐幾里得:
代碼:
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=1(6,4,x,y)
d = 6;
b != 0;
d = 4y + 2x;(4,2,y,x)//調用函數,{}裏都是函數執行的內容,注意這裏是y,x
{
d = 4;
b != 0; //if裏的判定
d = 2x(2,0,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利用已知就可以得到了。