zoukankan      html  css  js  c++  java
  • 扩展欧几里得学习笔记

    最近写题的时候经常遇到一些数论题,似乎是扩欧又似乎不是,为了加深自己的印象并且整理一下以前模糊的思路,就写下这篇笔记。

    感谢巨巨的博客:https://www.cnblogs.com/KatouKatou/p/9818175.html

    同时其中部分证明参考了柯召 孙琦编著的《数论讲义》第二版。

    欧几里得算法

    在讨论扩展欧几里得算法前先厘清一下欧几里得算法,也就是我们常说的辗转相除法。熟练掌握的人可以跳过,不过如果不知道如何证明的话可以看一下证明过程,说不定会有某种程度上的启发。

    用途

    辗转相除法的应用范围很单一,就是用来求两个整数的最大公因数的。

    过程

    假设我们要求的是a和b的最大公因数,设a=b*q1+r1,如果r1!=0,那么我们使a=b,b=r1,那么新的a和b必然也有类似的关系a=b*q2+r2,重复这个操作直到这个算式中的r(a/b的余数)等于0时,那个式子中的b就是原始的a,b两数的最大公因数。配合代码理解一下。

    int gcd(int a,int b)
    {
        return b?gcd(b,a%b):a;
    }

     

    证明

      在看过程中可能会有一个疑问:为什么我们可以这样子不断变换a,b的值?这就需要先证明一个先导定理:设a,b,c是三个不全为0的整数,且a=b*q+c ①,其中q是整数,则a与b的最大公因数等于b与c的最大公因数。

    为了方便表述,我在这里引用一个符号(a,b)来表示a和b的最大公因数。

    证:

        因为(a,b)整除与a,也整除于b,所以由①式我们可以得到(a,b)也整除于c(这里就不证明了),因而(a,b)<=(b,c)(因为(a,b)同时是b和c的约数,也就是说它是b和c的公约数,那么它必然小于等于b和c的最大公约数)。用同样的方法我们可以得到(a,b)>=(b,c),于是我们就可以得到(a,b)=(b,c)的结论,证毕。

     

      然后我们来证明辗转相除法,其实在证明之前还有一个我们常常默认为正确的定理需要证明(设a,b为两个整数且b>0,则存在两个唯一整数q,r使得 a=b*q+r,0<=r<b),不过一直在这种地方喋喋不休也很烦吧,我就略过不证了,有兴趣的朋友可以自己推导一下,并没有什么难度。

      其实这回的证明只要在原来的推导过程中加入一个范围就行。

      首先我们有a=b*q1+r1,0<r1<b。(r1如果等于0也就是a被b整除,那就没有证明的必要了,所以为了使证明更清晰我们假设前几式的余数均不为0)

      然后有b=r1*q2+r2,0<r2<r1,

                 r1=r2*q3+r3,0<r3<r2

                ......

                rn-2 = rn-1 * qn + rn,0 < rn <rn-1

                rn-1 = rn * qn+1 + rn+1,rn+1= 0

     可以看到余数r的值是不断减少的,那么在有限次的交换后r必定会变成0,我们假设r n+1=0,那么我们就可以得到rn=(0, rn)=(rn,r n-1)=......=(r2,r1)=(r1,b)=(b,a)。证毕。

    扩展欧几里得

     证明

      扩展算法的形式就是 对于任给的a>0,b>0,有a*x+b*y=(a,b)。这个结论其实我们用前面证明欧几里得算法的过程就可以得出。

      在前面证明欧几里得算法的时候我们列出了一连串的式子,然后我们把它们变换一下,从倒数第二式开始,我们可以得到rn-2 - rn-1 * qn = rn,

      同理 rn-1= rn-3 - rn-2 * qn-1,合并两式 可以得到 rn = rn-2 * (1+ qn * qn-1)-rn-3 * qn,我们再将rn-2 = rn-4 - rn-3 *qn代入,如此反复,最后就可以得到

      rn =a*x+b*y 即(a,b)=a*x+b*y,其中x,y是两个整数。

      解法

      对于a>0,b>0,有a*x1+b*y1=(a,b),

                同样可得b*x2+(a%b)*y2=(b,a%b)=(a,b)

         所以可以得到a*x1+b*y1=b*x2+(a-[a/b]*b)*y2

                           即a*x1+b*y1=a*y2+b*(x2-[a/b]*y2)

      那么由恒等定理可以得到x1=y2,y1=x2-[a/b]*y2。

     ([a/b]表示小于等于a/b的最大整数)

      至此我们就得到了一个递推式,而由前面欧几里得算法的推导过程我们知道,在推导到边界的时有b=0,return a,即gcd(a,0)=a,那么就有         a*xn+b*yn=a,显然此时xn=1,yn=0。

      到此为止,我们就求出了这个方程的一个特解。

      而在实际的应用过程中,等式的右边不一定为(a,b),而可能为一个任意的整数c,这时该如何求解?

      首先根据我们已经得出的结论,我们可以断言:如果c%(a,b)!=0,那么这个式子是肯定无解的。相对的,假如c%(a,b)=0并且c/(a,b)=t,那么我们只需要在求出的解上乘上一个t就行了。

     

      同时我们对x,y可能也有要求,该如何得到不同的使等式成立的x,y呢。可以想到的是对x,y进行加减操作,同时控制变化量保证等式恒等,显然假如x加上b,y减去a,那么等式是成立的,但这样我们无法保证变化量最小,也就是说我们不能保证这样加减可以得到所有可能的解。而通过观察之后,我们可以想到,x和y分别的最小的变化量应当分别是b/gcd(a,b)和a/gcd(a,b)。如此扩展欧几里得的求解就结束了。

    最后附上代码

     1 int exgcd(int a,int b,int &x,int &y)
     2 {
     3     if(!b)
     4     {
     5         x=1;
     6         y=0;
     7         return a;
     8     }
     9     int x2,y2;
    10     int ans=exgcd(b,a%b,x2,y2);
    11     x=y2;
    12     y=x2-(a/b)*y2;
    13     return ans;
    14 }

    代码的返回值就是a,b的最大公因数

      

  • 相关阅读:
    Python学习系列之类与对象(二十三)
    面向过程和面向对象的异同点
    js 数值精确运算使用math.js
    js实现复制 、剪切功能-clipboard.min.js 示例
    css div嵌套层中button的margin-top不起作用解决方法
    IPhone中H5页面用on绑定click无效的解决方法
    This is a good start.
    element之input输入搜索联想框
    vue + element-ui 国际化实现
    async/await 处理多个网络请求同步问题
  • 原文地址:https://www.cnblogs.com/forever3329/p/11322303.html
Copyright © 2011-2022 走看看