擴展歐幾里得(exgcd)的不才之論

關於exgcd(擴展歐幾里得定理):

由擴展歐幾里德定理,可以通過擴展歐幾里德算法求解線性同餘方程

擴展歐幾里德定理(百度百科)

對於不完全爲 0 的整數 a,b,gcd(a,b)表示 a,b 的最大公約數。那麼一定存在整

數 x,y 使得 gcd(a,b)=ax+by。

即 ax +by = gcd(a, b); 一定存在整數解(x, y);

 

題設:

已知a,b,設方程組:ax + by = gcd(a, b) , 求一組(x, y)使得該式成立。

 

推導過程:

由歐幾里得定理可知:

由:ax1+by1 = gcd(a, b) = gcd(b, a%b) = bx2 + (a%b)y2 ;

而 :bx2 + (a%b)y2  = bx2 + (a - a/b * b)y2 ;

整理可得:

x1a + y1b = y2a + (x2 - a/b*y2)b

由a, b的係數相同和待定係數法可知:

x1 = y2;

y1 = x2 - a/b*y2;

這樣它就將a與b的線性組合化簡爲b與a%b的線性組合。

因爲a和b都在不斷減小,gcd(a, b) = gcd(b, a%b) 當b==0 時,a即爲gcd(a, b);

所以,當b == 0;時,存在遞歸終點,此時,xn=1, yn=0;

然後遞歸回去就可以求出最終的x1和y1了

代碼:

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int gcd = exgcd(b, a%b, x, y);
    int t = x;
    x = y;
    y = t-a/b*y;
    return gcd;
}

解出的(x1, y1)爲ax + by = gcd(a, b)的一組解。

 

題設:

已知a,b,c,設方程組: ax + by = c, 求一個最小正整數解x,使得該方程成立。

 

推導過程:

由上述擴展歐幾里得定理可知:c = n*gcd(a, b) (n爲整數)

即原式變爲:ax + by = gcd(a, b) *n (n爲整數)。[即n = c/gcd(a, b)]

 

由上個題設exgcd可求得一組解(x1, y1)

即求得一組(x1, y1)使得 :

                                                 ax1 + by1 = gcd(a, b)                               ------------------------------------------------  (1)

令gcd = gcd(a, b);  

因爲gcd = gcd(a, b) 所以, a*x1/gcd是整數,b*y1/gcd是整數, 所以c/gcd也需要是整數,否則無解。

 

讓上述(1)式兩邊都乘(c/gcd)

得到:a*(x1*(c/gcd)) + b*(y1*(c/gcd)) = gcd * (c/gcd)

化簡得:a*(x1*(c/gcd)) + b*(y1*(c/gcd)) = c       --------------------------  (2)

 

即原式中的解(x, y)可有exgcd後的一組解(x1, y1)表示爲:

x = x1*(c/gcd)

y = y1*(c/gcd)

 

 

此時的x1*(c/gcd)是最小的解,但有可能是負數。

因爲 a*(x1*(c/gcd) + b*n) + b*(y1*(c/gcd) - a*n) = c (n是自然數)

所,x的最小正整數解爲:

x = (x1*(c/gcd)%b+b)%b

 

反過來可得最小的y的正整數解爲:

y = (y1*(c/gcd)%a+a)%a

 

這裏給出一個爲什麼x = (x1*(c/gcd)%b+b)%b是最小正整數的推導:

代碼:

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
       x=1;y=0;
       return a;
    }
    int r=exGcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
    return r;
}
int main()
{
    int a, b, c;
    while(cin >> a >> b >> c && a+b+c)
    {
        int x, y;
        int z = exgcd(a, b, x, y);
        cout << x << ' ' << y << ' ' << z << '\n';
 
        x*=c/z;     //一組解x,y
        y*=c/z;
        cout << x << ' ' << y << '\n';
 
        int t = b/z;        //求ax+by = c 的最小正整數x,b/z是(2)式約掉c之後的。。
        x = (x%t+t)%t;
        y = abs((a*x - c)/b);
 
        t = a/x;             //求ax+by = c 的最小正整數y
        y = (y%t+t)%t;
        x = abs((b*y - c)/a);
        cout << x << ' ' << y << '\n';
    }
    return 0;

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章