更新日誌
2024/12/08:開工。
歐幾里得演算法
用途與原理
歐幾里得演算法,用於求兩個數的最大公約數。
其核心原理為:\(\gcd(a,b)=\gcd(b,a\bmod b)\)
證明
首先,我們證明 \(\gcd(a,b)=\gcd(b,a\bmod b)\)。
為了方便證明,這裡我們令 \(a>b\)。
\[\because \gcd(a,b)\mid a \text,\gcd(a,b)\mid b\\ \therefore \gcd(a,b)\mid a-\lfloor \frac{a}{b}\rfloor\cdot b\Leftrightarrow \gcd(a,b)\mid a\bmod b\\ \because \gcd(a,b)\mid b,\gcd(a,b)\mid a\bmod b\\ \therefore \gcd(a,b)\mid \gcd(b,a\bmod b) \]
\[\because \gcd(b,a\bmod b)\mid b,\gcd(b,a\bmod b)\mid a\bmod b\\ \therefore\gcd(b,a\bmod b)\mid a\bmod b+\lfloor \frac{a}{b}\rfloor\cdot b\Leftrightarrow \gcd(b,a\bmod b)\mid a\\ \because \gcd(b,a\bmod b)\mid b,\gcd(b,a\bmod b)\mid a\\ \therefore \gcd(b,a\bmod b)\mid \gcd(a,b) \]
證畢。
當 \(b\mid a\)時,顯然此時 \(\gcd(a,b)=b\)。
證畢。
實現細節
- 實際實現中,我們無需判斷 \(a>b\),類似於
if(a<b)swap(a,b)
的操作是不必要的。
假如 \(a<b\),那麼下一步遞迴時代入的 \(\gcd(b,a\bmod b)=\gcd(b,a)\),可見沒有影響,只多了 \(1\) 的常數。 - 更常見的寫法是
if(!b)return a
,當然if(a%b==0)return b
也沒有問題,不過你也可以看出前者更簡潔(實際沒有多少),只是會多出 \(1\) 的常數,但通常情況下%
都是很慢的,所以前者其實並不比後者劣。
模板
int gcd(int a,int b){return (b?gcd(b,a%b):a);}
如果你不喜歡三目運算子或者壓行:
int gcd(int a,int b){
if(!b)return a;
return gcd(b,a%b);
}
擴充歐幾里得演算法
用途
擴充歐幾里得演算法用來求解形似 \(ax+by=c\) 的線性不定方程。
拓歐常見的利用是求乘法逆元。
原理與證明
首先,我們需要確定上述方程是否有解,根據裴蜀定理,上述方程有解當且僅當 \(\gcd(a,b)\mid c\)。
我們考慮如何藉助歐幾里得演算法求解這個方程。事實上,擴充歐幾里得演算法是用來求解 \(ax+by=\gcd(a,b)\) 的,但因為 \(\gcd(a,b)\mid c\),所以我們可以以此為跳板求出原方程的解。
思考 \(ax+by=\gcd(a,b)\) 中 \(x,y\) 與 \(bx'+(a\bmod b)y'=\gcd(b,a\bmod b)\) 中 \(x',y'\) 的關係:
這樣,我們在求 \(\gcd(a,b)\) 的同時,即可求出 \(x,y\)。
考慮邊界情況,當 \(b\mid a\) 時,方程式形如:
顯然此時一組可行解為 \(x=0,y=1\),然後回溯即可。
但並沒有結束,我們得到的是 \(ax+by=\gcd(a,b)\) 的解,令之為 \(x',y'\),不難發現原方程的解為:
實現細節
- 考慮到 \(\gcd\) 我們會在 \(b=0\) 時退出(如果不是這種寫法請忽略),此時應使 \(x=1,y=0\),這樣回溯一步就可以使 \(b|a\) 時 \(ax+by=b\) 的解為 \(x=0,y=1\)。
- 上述透過 \(x',y'\) 給 \(x,y\) 賦值的操作有更簡潔的寫法,可以直接向下傳參的時候 \(x'\gets y,y'\gets x\) 且使用引用傳參,返回之後 \(y\gets y-\lfloor\frac{a}{b}\rfloor\cdot x\) 即可。
模板
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}
求乘法逆元
使用 \(\text{exgcd}\) 求乘法逆元的前提是模數與被求的數互質。原因下面會說。
考慮乘法逆元定義:\(ax\equiv 1\pmod p\),其中 \(x\) 即為 \(a\) 逆元。
我們展開原式可得:
那麼就可以用拓歐求解了。根據裴蜀定理,有解的前提是 \(\gcd(a,p)\mid 1\),等同於 \(a,p\) 互質。
最後的 \(x\) 無需除以 \(\text{gcd}\) 然後再乘 \(1\),因為這兩個都是 \(1\)。
使用格式大概如下:
int x,y;
exgcd(a,p,x,y);
然後 \(x\) 就是 \(a\) 模 \(p\) 意義下的逆元了。