zoukankan      html  css  js  c++  java
  • 四大扩展欧几里得算法

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

  • 相关阅读:
    net.sf.json.JSONObject maven下载到了但是java后台一直用不了问题
    创建springboot2.1项目运行报错
    百度地图,加载顺序异步问题,用定时器解决
    大话设计模式--(1)简单工厂模式
    H5页面单点登录跳回首页 http url参数转义
    H5页面,百度地图点击事件
    批量给数据两边加上双引号和逗号
    java基础源码 (6)--ArrayListt类
    前端 移动端H5页面 DEBUG
    H5页面,华为手机打开不加载JS的问题
  • 原文地址:https://www.cnblogs.com/xiaofengzai/p/11254670.html
Copyright © 2011-2022 走看看