[数论]Gcd/ExGcd欧几里得学习笔记
\(Q\):什么是\(GCD\)?
- \(GCD\)
\(GCD\),即最大公約數(\(Greatest\ Common\ Divisor\))
對于兩個自然數\(a,b\),定義\(GCD(a,b)\)為\(a,b\)所有公共約數中最大的一個,即\(GCD(a,b)=\max\{x\in N^*,x|a\)且\(x|b\}\)
然后是關于\(GCD\)的算法即其擴展
\(Part1\) 歐幾里得算法
\(Q\):這個算法有什么用?
此算法可以在\(log\)級別里求出兩個數\(a,b\)的\(GCD\)
- 歐幾里得算法
對于\(a,b\),設它們的\(GCD\)為\(d\)。
則有:\(d|a\)且\(d|b\)
設\(a=k*{}b+r(k=\left\lfloor\frac{a}{b}\right\rfloor,r=a\mod b)\)
則:\(d|(a-k*{}b)\),即\(d|r\)
所以\(GCD(a,b)=GCD(b,r)=GCD(b,a\mod b)\)
最后遞歸求解,可在\(O(log_2a)\)內得解。
時間復雜度 \(O(log_2a)\)
空間復雜度 \(O(log_2a)\)(遞歸棧)
實際復雜度一般到不了此上界。
代碼:
//求GCD(a,b) #include <cstdio>int GCD(int a,int b) {return b?GCD(b,a%b):a;//當b為0時,GCD(a,0)=a,充當遞歸邊界。 }int main() {int a,b;scanf("%d%d",&a,&b);printf("%d\n",GCD(a,b));return 0; }\(Part2\) 擴展歐幾里得
\(Q\):這個奇怪的算法又是什么啊?
首先,先來看一個不定方程:
\[ax+by=c\]
擴展歐幾里得就是用來求此方程的不定解\((x,y)\)的。
準備知識:
- \(Bezout\)定理
對于兩個整數\(a,b\)存在一對整數\(x,y\),滿足\(ax+by=GCD(a,b)\)
證明:
在調用歐幾里得算法時,當\(b=0\),顯然有\(x=1,y=0\)滿足定理。
當\(b\not= 0\)時,若已經求得,\(x,y\),使得
\[bx+(a\mod b)y=GCD(b,a\mod b)\]
則
\[bx+(a-b\left\lfloor\frac{a}{b}\right\rfloor)y=GCD(b,a\mod b)\]
\[ay+b(x-\left\lfloor\frac{a}{b}\right\rfloor y)=GCD(b,a\mod b)\]
設\(x'=y,y'=x-\left\lfloor\frac{a}{b}\right\rfloor y\),則有
\[ax'+by'=GCD(b,a\mod b)=GCD(a,b)\]
利用數學歸納法,知道\(Bezout\)成立。
同時,我們可以利用此定理求出一組\(ax+by=GCD(a,b)\)的解。
時間復雜度 \(O(log_2a)\)
空間復雜度 \(O(log_2a)\)
代碼:
//求x,y使得ax+by=GCD(a,b),同時得到GCD(a,b) #include <cstdio>int ExGCD(int a,int b,int &x,int &y) {if(!b){x=1,y=0;return a;}//遞歸邊界int GCD=ExGCD(b,a%b,x,y);int Tmp=x;x=y,y=Tmp-a/b*y;return GCD; }int main() {int a,b,GCD,x,y;scanf("%d%d",&a,&b);GCD=ExGCD(a,b,x,y);printf("%d %d %d\n",GCD,x,y);return 0; }接著回到正題:對于方程\(ax+by=c\),它有解僅當\(GCD(a,b)|c\)。
證明?不會,記住就行,畢竟顯然。
接著,求出方程\(ax+by=GCD(a,b)\)的一組解,則:
\[ax+by=GCD(a,b)\]
\[\frac{c}{GCD(a,b)}*{}(ax+by)=c\]
\[a*\frac{cx}{GCD(a,b)}+b*\frac{cy}{GCD(a,b)}=c\]
于是就有了一組解:\(x'=\frac{cx}{GCD(a,b)},y'=\frac{cy}{GCD(a,b)}\)(對\(x,y\)同時乘上\(\frac{c}{GCD(a,b)}\))
于是問題得解。
時間復雜度 \(O(log_2a)\)
空間復雜度 \(O(log_2a)\)
代碼:
//求x,y使得ax+by=c,同時得到GCD(a,b) #include <cstdio>int ExGCD(int a,int b,int &x,int &y) {if(!b){x=1,y=0;return a;}//遞歸邊界int GCD=ExGCD(b,a%b,x,y);int Tmp=x;x=y;y=Tmp-a/b*y;return GCD; }int main() {int a,b,c,GCD,x,y;scanf("%d%d%d",&a,&b,&c);GCD=ExGCD(a,b,x,y);if(c%GCD)return puts("Orz I cannot find it!"),0;//無解int d=c/GCD;printf("%d %d %d\n",GCD,x*d,y*d);return 0; }關于通解以及\(x\)最小正整數解。
設\(d=GCD(a,b)\),
\(x_0,y_0\)為\(ax+by=GCD(a,b)\)的一組特解,
\(x=x_0+km,y=y_0+kn(k\in \mathbb{Z})\)為方程的通解表示(k為任意整數),
則:
\[ax+by=ax_0+by_0\]
\[a(x_0+km)+b(y_0+kn)=ax_0+by_0\]
\[am+bn=0\]
顯然,當\(m=-b,n=a\)時等式成立。
又因為要把\(m,n\)比例降到最小,所以同時除去\(GCD\)。
最后的通解表示為:
\[x=x_0-k\frac{b}ozvdkddzhkzd,y=y_0+k\frac{a}ozvdkddzhkzd(k\in \mathbb{Z})\]
同理,對于方程\(ax+by=c\)的通解為:
\[x=\frac{c}ozvdkddzhkzdx_0-k\frac{b}ozvdkddzhkzd,y=\frac{c}ozvdkddzhkzdy_0+k\frac{a}ozvdkddzhkzd(k\in \mathbb{Z})\]
所以求\(x\)的最小整數解只需要將\(x\mod \frac{b}ozvdkddzhkzd\)再推出\(y\)即可。
時間復雜度 \(O(log_2a)\)
空間復雜度 \(O(log_2a)\)
代碼:
//求ax+by=c的x最小正整數解。 #include <cstdio>int ExGCD(int a,int b,int &x,int &y) {if(!b){x=1,y=0;return a;}//遞歸邊界int GCD=ExGCD(b,a%b,x,y);int Tmp=x;x=y;y=Tmp-a/b*y;return GCD; }int main() {int a,b,c,d,x,y;scanf("%d%d%d",&a,&b,&c);d=ExGCD(a,b,x,y);if(c%d)return puts("Orz I cannot find it!"),0;//無解x*=c/d,y*=c/d;int f=b/d;x=(x%f+f)%f;y=(c-a*x)/b;printf("%d %d\n",x,y);//x最小正整數解。return 0; }所有性質證明到此結束。
歐幾里得是個好東西,然而我不會用。
數論博大精深,不能就此止步,還要繼續探索。
祝自己\(++OI::RP\)
轉載于:https://www.cnblogs.com/LanrTabe/p/10370627.html
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的[数论]Gcd/ExGcd欧几里得学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。