四大扩展欧几里得算法

扩展欧几里得算法1.ax+by=gcd(a,b)

扩展欧几里得算法来解决这样一个问题:给定两个非零整数a和b,求一组整数解(x,y),使得ax+by=gcd(a,b)成立,其中gcd(a,b)表示a和b的最大公约数。

gcd(a,b)-->gcd(b,a%b)-->.....--->gcd(a,0)最终返回a的值,所以就有一组解成立,a*1+b*0=gcd。此时有x=1,y=0

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

当计算gcd(a,b),有ax1+by1=gcd成立;下一步计算gcd(b,a%b)时,有bx2+(a%b)y2=gcd成立。

因此ax1+by1=bx2+(a%b)y2成立,化简后

x1=y2

y1=x2-(a/b)y2

因此就有啦如下代码:

#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的最大公因数
}

得到最原始的解后,就可以求全部解

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    扩展欧几里得算法的理解

posted @ 2019-07-27 13:17  清风紫雪  阅读(761)  评论(0编辑  收藏  举报