淺談擴展歐幾里得(Exgcd)


前言

假設我們已經會了歐幾里得算法
而且,真真真真的是淺談

基本形式

\[ax+by=\gcd(a,b) \]

\[a, b\in \mathbb{N}^* \]

擴展歐幾里得 (Exgcd) 則是求解以上方程的整數解

求特解

觀察基本形式 \(ax+by=\gcd(a,b)\)
並考慮歐幾里得算法的結束條件 \(a=gcd, b=0\)
則最終狀態是 \(x=1, y=0\)
繼續分析條件

\[\because \gcd(a, b)=\gcd(b, a \% b) \]

\[\therefore bx'+(a\% b)y'=\gcd(b, a\% b) \]

假設我們已經求解了

\[bx'+(a\% b)y'=\gcd(b, a\% b) \]

並且要求出他的上一狀態

\[ax+by=\gcd(a,b) \]

我們知道

\[a\% b=a-\left \lfloor \frac{a}{b} \right \rfloor b \]

所以

\[\begin{array}{lcl} \begin{aligned} \gcd(a, b) & = bx'+(a-\left \lfloor \frac{a}{b} \right \rfloor b)y' \\ & = bx'+ay'-\left \lfloor \frac{a}{b} \right \rfloor by' \\ & = ay'+(x'-\left \lfloor \frac{a}{b} \right \rfloor y')b \\ \end{aligned} \\ \because ax+by=\gcd(a, b) \\ \therefore x=y',\ y=x'-\left \lfloor \frac{a}{b} \right \rfloor y' \end{array} \]

最后遞歸求解即可

根據特解求通解

以下式為例,並假設有解,即 \(\gcd(a, b) | c\)

\[ax+by=c \]

我們已知一組 \(ax+by=\gcd(a,b)\) 的特解,記為 \(x_0, y_0\)
則有

\[\begin{array}{rcl} ax_0+by_0&=&\gcd(a,b)\\ a\frac{x_0c}{\gcd(a,b)}+b\frac{y_0c}{\gcd(a,b)}&=&c\\ \end{array} \]

於是我們找到了原方程的一組特解,記為 \(x_1\)\(y_1\)

\[x_1=\frac{x_0c}{\gcd(a,b)},\ y_1=\frac{y_0c}{\gcd(a,b)} \]

於是我們設任意 \(d\in \mathbb{Q}\),那么一定有

\[a(x_1+db)+b(y_1-da)=c \]

\(x_1+db\)\(y_1-da\) 必須為整數
又因為 \(x_1,\ y_1\) 為整數,所以我們只需要保證 \(db, da\in \mathbb{Z}\)
令當 \(d\) 取到最小的可能的正值時的 \(d_x=db, d_y=da\),那么任意解中這兩個變量與 \(x_1,\ y_1\) 的偏差一定分別為 \(d_x\)\(d_y\) 的倍數
那么顯然最小 \(d=\frac{1}{\gcd(a, b)}\) ,所以 \(d_x=\frac{b}{\gcd(a, b)},\ d_y=\frac{a}{\gcd(a, b)}\)
於是通解形式為

\[\begin{array}{rcl} x&=&x_1+kd_x\\ y&=&y_1-kd_y \end{array} \]

其中,\(k\) 是任意整數

例題

P5656 【模板】二元一次不定方程 (exgcd)
以上我們已經求出通解形式,於是分類討論即可(注意細節)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
using namespace std;
#define fore(i,now) for(reg int i=head[now];i;i=e[i].nxt)
#define fora(i,a,b,c) for(reg int i=a;i<=b;i+=c)
#define forb(i,a,b,c) for(reg int i=a;i>=b;i-=c)
#define uLL unsigned LL
#define INF 0x3f3f3f3f
#define LL long long
#define reg register
#define R read()
inline LL read(){
    LL s=0, f=1; char c=getchar();
    while(c<'0'||c>'9') { if(c=='-') f=-f; c=getchar(); }
    while(c>='0'&&c<='9') s=(s<<3)+(s<<1)+(c^48), c=getchar();
    return s*f;
}
LL Exgcd(LL a, LL b, LL &x, LL &y){
    if(b==0) { x=1, y=0; return a; }
    LL g=Exgcd(b, a%b, x, y);
    LL t=x; x=y, y=t-(a/b)*y;
    return g;
}
int main(){
    int T=R; while(T-->0){
        LL a=R, b=R, c=R, x, y, k;
        LL g=Exgcd(a, b, x, y);
        /* 判斷有無解 */
        if(c%g) { puts("-1"); continue; }
        /* 求 x1 及 y1 */
        x*=c/g, y*=c/g; 
        /* q, p 同 dx, dy */
        LL q=b/g, p=a/g; 
        /* 將 x 調整為最小正整數解 */
        if(x<=0) k=ceil((1.0-x)/q), x+=k*q, y-=k*p;
        else k=(x-1)/q, x-=k*q, y+=k*p;
        /* 判斷有無正整數解 */
        if(y>0)
            /* 根據詢問回答即可, (y-1)%p+1 是為了使 y 是正整數 */
            printf("%lld %lld %lld %lld %lld\n", (y-1)/p+1, x, (y-1)%p+1, x+(y-1)/p*q, y);
        else
            printf("%lld %lld\n", x, y+(LL)ceil((1.0-y)/p)*p);
    }
    return 0;
}

后記

參考 dengyaotriangle 的博客 - 題解 P5656 【【模板】二元一次不定方程(exgcd)】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM