概述
擴充套件歐幾里得演算法 (\(exgcd\)) 可以用來求形如 \(ax+by=c\) 的不定方程的通解。
鋪墊 - \(\small\texttt{ax+by=gcd(a,b)}\)的解
\(exgcd\) 的思想是在用輾轉相除法遞迴 \(gcd(a,b)\) 的回溯時求出對應方程 \(ax+by=gcd(a,b)\)的解。
考慮方程 \(ax+by=gcd(a,b)\) 。
看回輾轉相除法的程式碼,我們將他展開一點寫。
void gcd(int a,int b){
if(!b) return a;
return gcd(b,a%b);
}
注意最後當 \(b=0\) 時,\(gcd(a,b)=gcd(a,0)=a\),此時當 \(\begin{cases}x=1\\y=0\end{cases}\) 時,\(ax+by=gcd(a,b)\),於是最終狀態下,\(\begin{cases}x=1\\y=0\end{cases}\) 即為其對應方程的解。
接下來,假設我們已經知道了 \(bx+(a\text{ }mod\text{ }b)y=gcd(b,a\text{ }mod\text{ }b)\) 的一組特解 \(\begin{cases}x=x'\\y=y'\end{cases}\),我們嘗試求出 \(ax+by=gcd(a,b)\) 的解。
\(\because ax+by=gcd(a,b)\text{ },\text{ }bx'+(a\text{ }mod\text{ }b)y'=gcd(b,a\text{ }mod\text{ }b)\)
\(\small{且}\text{ }gcd(a,b)=gcd(b,a\text{ }mod\text{ }b)\)
\(\therefore ax+by=bx'+(a\text{ }mod\text{ }b)y'\)
\(\therefore ax+by=bx'+(a-\lfloor\frac{a}{b}\rfloor b)y'\)
\(\small{【變更主元】 得:}\)
\(a(x-y')+b(y-x'+\lfloor\frac{a}{b}\rfloor y')=0\)
\(\small{若想使上式總是成立,只需使:}\)
\(\begin{cases}x-y'=0\\y-x'+\lfloor\frac{a}{b}\rfloor y'=0\end{cases}\)
\(\therefore\begin{cases}x=y'\\y=x'-\lfloor\frac{a}{b}\rfloor y'\end{cases}\)
於是就完成了轉換/轉移,翻譯成程式碼:
// 不需要知道 gcd(a,b) 是多少,無需返回值
void exgcd(int a,int b,int &x,int &y){ // 傳址,直接修改
if(!b){
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
int tmp=x;
x=y,y=tmp-(a/b)*y; // 直接修改
}
簡便寫法:
void exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return;
}
exgcd(b,a%b,y,x); // 區別:交換x和y的位置,等同於swap(x,y)
y-=(a/b)*x;
}
擴充套件 - \(\small\texttt{ax+by=c}\)的解
先來看一個定理:
裴蜀定理:\(ax+by=c\) 有整數解的充要條件是 \(gcd(a,b) \mid c\)
這個定理是顯然的,至少你從直覺上就覺得它沒問題。
現在我們知道怎麼判無解了,那麼接下來的探究都建立在 \(ax+by=c\) 有界的情況下,那我們開始吧。
記 \(g=gcd(a,b).\)
\(\therefore g \mid a,g \mid b\)
\(\small{由裴蜀定理:}\)\(g \mid c\)
記 \(a'=\frac{a}{g},b'=\frac{b}{g},c'=\frac{c}{g}.\)
\(\therefore gcd(a',b')=1\)
發現可以用 \(exgcd\) 求出 \(a'x+b'y=1\) 的解\(\begin{cases}x=x'\\y=y'\end{cases}\)!
\(\therefore a'x+b'y=c'\)\(\text{ }\small{的解為}\)\(\begin{cases}x=c'x'\\y=c'y'\end{cases}\)
\(\therefore a'gx+b'gy=c'g\)\(\text{ }\small{即}\text{ }\)\(ax+by=c\)\(\text{ }\small{的解為}\)\(\begin{cases}x=c'x'\\y=c'y'\end{cases}\)
這樣我們就找到了一組特解 \(\begin{cases}x=c'x'\\y=c'y'\end{cases}\).
通解即為 \(\begin{cases}x=c'x'+b'k\\y=c'y'-a'k\end{cases}(k \in \mathbb{Z})\).
(證明、程式碼略)
Tips:
C++
對負數取模的處理與數學中不一樣,如數學中的 \(a\text{ }mod\text{ }b(a \in \mathbb{Z^-})\) 在C++
中的值為 \(-(abs(a)\text{ }mod\text{ }b)\).
\(eg:\)
數學:\(-5\text{ }mod\text{ }3=1\)
C++
:\(-5\text{ }mod\text{ }3=-2\)
為解決這樣的問題,我們一般這麼寫:ans=(a%b+b)%b