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

    拓展欧几里得算法

    先来看看一个重要的基本定理

    裴蜀定理

    对于整数a,b,他们关于x,y的线性不定方程(ax+by=d),设(gcd(a,b)=g),则可证明(g|d),换句话说,就是g是a,b的最小线性组合

    证明:

    (ax+by=d)(g=gcd(a,b)),设(ax+by)的最小值为s,

    (ecause) (g|a,g|b)

    ( herefore) (g|d),(g|s)

    (q=[frac{a}{s}].则r= amod s=a-q*s=a-q*(ax+by)=a(1-qx)+b(-qy))

    可见r也是x,y的线性组合,又r是s的余数,(rin [0,s-1]),又s是最小线性组合,( herefore r=0)

    推出(s|a),同理有(s|b),则(s|g),又已经有(g|s),所以(g=s)。可知g是ax+by的最小线性组合。

    推论:

    a和b互质的充要条件是存在x,y使(ax+by=1),因为由上面的证明结论知道d(这里是1)一定是gcd(a,b)的倍数。

    其实还可以推广到一堆数互质(这些数的gcd为1)的情况。

    拓展欧几里得

    我们知道,一般的 欧几里得算法是来求两个数的最大公因数的,但是拓展欧几里得可以走得更远,也就是说,它不仅仅求一个gcd,而是可以求出一些额外的东西。前面的定理说了a,b线性组合最小是(gcd(a,b)),我们就可以求出(ax+by=gcd(a,b))对应的x,y出来。

    怎么求呢?我们已经知道当欧几里得算法递归到终点时,gcd(a,b)中的b已经是零,也就是说,这个时候(a*1+b*0=a),x=1,y=0,我们就可以在这时反推上去求解最终的x,y

    现在是递归返回过程的某一步,我们已经求得了b和a%b的gcd,还有此时的x1,y1,就是说(b*x1+a\%b*y1=gcd),要怎么求a,b的x,y呢?

    我们知道(a\%b=a-[frac{a}{b}]*b)(gcd=b*x1+(a-[frac{a}{b}]*b)*y1=b*x1+a*y1-[frac{a}{b}]*y1*b=a*y1+(x1-[frac{a}{b}]*y1)*b=a*x+b*y)

    可以看出(x=y1,y=x1-[frac{a}{b}]*b).递归返回时即可更新掉x,y的值返回即可。

    代码:

    int ex_gcd(int a,int b,int& x,int& y)
    {
        if(b==0)
        {
            x=1;
            y=0;
            return a;
        }
        int ans = ex_gcd(b,a%b,x,y);
        int tmp = x;
        x = y;
        y = tmp-a/b*y;
        return ans;
    }
    

    拓展欧几里得求逆元

    什么是逆元?(a*xequiv{1}mod m),这里(x)就是a的逆元。逆元有什么用呢?*如果我们要求(a/b mod m)的值,而a,b很大,设b的逆元为x,这个时候注意到((a/b)*x*b=(a/b)mod m=a*xmod m)巧妙地把出发转换成了乘法。

    为什没求逆元跟欧几里得算法联系起来了呢?根据上面裴蜀定理,我们知道gcd是a,b两个数线性组合的最小值,其他组合值都是gcd的倍数,当gcd为1时,a,b互质,满足(ax+by=1),移项得(ax=-by+1),即(axequiv1mod b),此时的x就是逆元。实际上线性不定方程组有无穷多解,这里只求正的最小的逆元。

    代码

    int cal(int a,int m)
    {
        int x,y;
        int gcd = ex_gcd(a,m,x,y);
        //cout << "a " << a << " m " << m << " x " << x << " y " << y << endl;
        if(1%gcd!=0) return -1;
        x*=1/gcd;
        m = abs(m);
        int ans = x%m;
        if(ans<=0) ans += m;
        return ans;
    }
    

    这里1%gcd是看gcd是不是1,前面说了,d(这里是1)应该是gcd的倍数,而且不互质的两个数没有逆元。(x*=1/gcd)实际上更一般的写为(x*=d/gcd),也就是求解一般不定方程(ax+by=d)的解,因为d是gcd的倍数,我们就把倍数乘上去解得(x'=x*(d/gcd))。m是负数的话,我们取|m|,如果求出来x是负数,就x%|m|,结果再加上|m|即可。(因为有无穷个解,通解为(x+m*t)

    参考文章

    zhj5chengfeng,扩展欧几里德算法详解,https://blog.csdn.net/zhjchengfeng5/article/details/7786595

    leader_one,欧几里得算法/扩展欧几里得算法,https://blog.csdn.net/leader_one/article/details/75222771

    Jollwish,扩展欧几里得算法,https://wenku.baidu.com/view/a75fdfd376a20029bd642de5.html

  • 相关阅读:
    android头像更换(实现拍照和从手机图片里选择两种形式)
    安卓开发实战-记账本APP(六)
    安卓开发实战-记账本APP(五)
    安卓开发实战-记账本APP(四)
    安卓开发实战-记账本APP(三)
    BaseAdapter的三种表达式分析,startActivityForResult的使用
    使用Bundle在Activity之间交换数据
    深入理解JVM-类加载器深入解析(3)
    深入理解java内存模型--读书笔记
    深入理解JVM-类加载器深入解析(2)
  • 原文地址:https://www.cnblogs.com/zhanhonhao/p/11329772.html
Copyright © 2011-2022 走看看