四大擴展歐幾里得算法

擴展歐幾里得算法1.ax+by=gcd(a,b)ios

擴展歐幾里得算法來解決這樣一個問題:給定兩個非零整數a和b,求一組整數解(x,y),使得ax+by=gcd(a,b)成立,其中gcd(a,b)表示a和b的最大公約數。c++

gcd(a,b)-->gcd(b,a%b)-->.....--->gcd(a,0)最終返回a的值,因此就有一組解成立,a*1+b*0=gcd。此時有x=1,y=0算法

利用上面的式子反推最原始的x和y。spa

當計算gcd(a,b),有ax1+by1=gcd成立;下一步計算gcd(b,a%b)時,有bx2+(a%b)y2=gcd成立。code

所以ax1+by1=bx2+(a%b)y2成立,化簡後blog

x1=y2遞歸

y1=x2-(a/b)y2ci

所以就有啦以下代碼:it

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

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 temp=x;
    x=y;    //把x y變成上一層的
    y=temp-(a/b)*y;
    return r;     //獲得a b的最大公因數
}

獲得最原始的解後,就能夠求所有解io

x'=x+(b/gcd)*k

y'=y+(a/gcd)*k

最小非負整數解:(x%(b/gcd)+b/gcd)%(b/gcd)

例題:https://www.acwing.com/problem/content/879/

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

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 temp=x;
    x=y;    //把x y變成上一層的
    y=temp-(a/b)*y;
    return r;     //獲得a b的最大公因數
}
int main()
{
    int a,b,x,y,r,t;
    cin>>t;
    while(t--)
    {
        cin>>a>>b;
        r=exgcd(a,b,x,y);
        cout<<x<<" "<<y<<endl;
    }
    return 0;
}

若是ax+by=1時,x的最小非負整數就是:(x%b+b)%b

擴展歐幾里得算法2.ax+by=c求解

已知ax+by=gcd的解,那麼兩邊同時乘上c/gcd,即爲ax+by=c的解

所以(x,y)=(cx0/gcd,cy0/gcd),這樣作的充分條件是c%(gcd(a,b))==0

通解:

cx0/gcd+b/gcd*k

cy0/gcd-a/gcd*k

那麼他的最小整數解爲:

x=((cx/gcd)%(b/gcd)+b/gcd)%(b/gcd)

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;
int gcd(int a,int b)
{
    if(b==0)
        return a;
    return gcd(b,a%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 temp=x;
    x=y;    //把x y變成上一層的
    y=temp-(a/b)*y;
    return r;     //獲得a b的最大公因數
}
int main()
{
    int c,r,a,b,x,y;
    cin>>a>>b>>c;
    r=exgcd(a,b,x,y);
    cout<<(c*x/r%(b/r)+b/r)%(b/r)<<endl;
    return 0;
}

擴展歐幾里得算法3.同餘式:ax=c(mod m)的求解

ax=c(mod m)= ( ax-c )%m==0

ax-c=my,令Y=-Y,得

ax+my=c,當c%gcd(a,m)==0時成立,纔有解。

解的形式爲(x,y)=(cx0/gcd(a,m),cy0/gcd(a,m))

通解爲:

x'=x+m/gcd(a,m)*k

y'=y-a/gcd(a,m)*k

最小正整數解爲:x=((cx0/gcd(a,m)%(m/gcd(a,m))+m/gcd)%m/gcd;

例題:線性同餘方程https://www.acwing.com/problem/content/880/

#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;
long long gcd(long long a,long long b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}
long long exgcd(long long a,long long b,long long &x,long long &y)//擴展歐幾里得算法
{
    if(b==0)
    {
        x=1;y=0;
        return a;  //到達遞歸邊界開始向上一層返回
    }
    long long r=exgcd(b,a%b,x,y);
    long long temp=x;
    x=y;    //把x y變成上一層的
    y=temp-(a/b)*y;
    return r;     //獲得a b的最大公因數
}
int main()
{
    long long a,b,x,y,r,m,k,t;
    cin>>t;
    while(t--)
    {
        cin>>a>>b>>m;
        if(b%gcd(a,m)==0)
        {
            r=exgcd(a,m,x,y);
            cout<<(((b*x)/r)%(m/r)+m/r)%(m/r)<<endl;
        }
        else
            cout<<"impossible"<<endl;
    }
    return 0;
}

擴展歐幾里得算法4.逆元的求解以及(b/a)%m的計算

當計算(b/a)%m的值時,咱們須要先算出a模m的逆元x,

就有(b/a)%m=(b*x)%m成立

a模m的逆元就是同餘式:ax=1(mod m)

若是gcd(a,m)=1,就有解,求出x後,就能夠用(x%m+m)%m獲得所須要的逆元。

//在a和m互不是質數的狀況下:(b/a)%m=(b%(am))/a

/*ab=1(mod m);
計算(b/a)%m=(b*x)%m其中x爲a模m的逆元
接下來求解ax=1(mod m)
是否有解取決於 1%gcd(a,m)==0
也就是 gcd(a,m)==1則有解。
求出x,在利用(x%m+m)%m)求出最小解
*/
#include<iostream>
#include<cstdio>
#include<cmath>

using namespace std;

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 temp=x;
    x=y;    //把x y變成上一層的
    y=temp-(a/b)*y;
    return r;     //獲得a b的最大公因數
}
int main()
{
    int a,b,x,y,m,r;
    cin>>a>>b>>m;
    r=exgcd(a,m,x,y);
    x=(x%m+m)%m;
    cout<<(b*x)%m<<endl;
    return 0;
}

固然也能夠使用費馬小定理:am-2%m  就是a模m的逆元,再用快速冪便可求解

例題:快速冪求逆元https://www.acwing.com/problem/content/878/

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,a,p;
ll gcd(ll a,ll b)
{
    if(b==0)
    return a;
    return gcd(b,a%b);
}
ll qpow(ll n,ll x)
{
    ll ans=1;
    while(x)
    {
        if(x&1)
        ans=ans*n%p;
        n=n*n%p;
        x>>=1;
    }
    return ans;
}
int main()
{
    ll i,j;
    cin>>t;
    while(t--)
    {
        cin>>a>>p;
        if(gcd(a,p)==1)
            cout<<qpow(a,p-2)%p<<endl;
        else
            cout<<"impossible"<<endl;
    }
    return 0;
}

若是a和m互不質,能夠用公式(b/a)%m=(b%(a*m))/a

證實:存在整數k,使得b/a=km+x,兩邊同時乘以a,得b=kam+ax,因而b%(am)=ax,兩邊同時除以a得,(b%(a*m))/a=x,因而等式成立

注意:a和m的乘積可能太大而溢出。

2019.7.27    擴展歐幾里得算法的理解

相關文章
相關標籤/搜索