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

    乘法逆元

    定义: 满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元。
    为什么要有乘法逆元呢?
    当我们要求(a/b) mod p的值,且a很大,无法直接求得a/b的值时,我们就要用到乘法逆元。 我们可以通过求b关于p的乘法逆元k,将a乘上k再模p,
    即(a*k) mod p。其结果与(a/b) mod p等价。
    证:(其实很简单。。。) 根据b*k≡1 (mod p)有b*k=p*x+1。 k=(p*x+1)/b。 把k代入(a*k) mod p,
    得: (a*(p*x+1)/b) mod p =((a*p*x)/b+a/b) mod p =[((a*p*x)/b) mod p +(a/b)] mod p =[(p*(a*x)/b) mod p +(a/b)] mod p //p*[(a*x)/b] mod p=0 所以原式等于:(a/b) mod p

    其它好的博客:http://www.cnblogs.com/yefeng1627/archive/2012/12/24/2830594.html

    http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html#2985941

    扩展欧几里德算法是用来在已知a, b求解一组p,q使得p * a+q * b = Gcd(p, q) (解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。下面是一个使用C++的实现:
      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 t = x;
          x = y;
           y = t - a / b * y;
          return r;
      }
      把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。
      可以这样思考:
      对于a' = b, b' = a % b 而言,我们求得 x, y使得 a'x + b'y = Gcd(a', b')
      由于b' = a % b = a - a / b * b (注:这里的/是程序设计语言中的除法)
      那么可以得到:
      a'x + b'y = Gcd(a', b') ===>
      bx + (a - a / b * b)y = Gcd(a', b') = Gcd(a, b) ===>
      ay +b(x - a / b*y) = Gcd(a, b)
      因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b*y)
      补充:关于使用扩展欧几里德算法解决不定方程的办法
      对于不定整数方程pa+qb=c,若 c mod Gcd(a, b)=0,则该方程存在整数解,否则不存在整数解。
      上面已经列出找一个整数解的方法,在找到p * a+q * b = Gcd(a,b)的一组解p0,q0后,p * a+q * b = Gcd(a, b)的其他整数解满足:
      p = p0 + b/Gcd(a, b) * t 
      q = q0 - a/Gcd(a, b) * t(其中t为任意整数)
      至于pa+qb=c的整数解,只需将p * a+q * b = Gcd(a, b)的每个解乘上 c/Gcd(a, b) 即可。

    更通常的是:我们需要求解方程的最小整数解
    若我们已经求得x0,y0为方程中x的一组特解,那么
    x=x0+b/gcd(a,b)*t,y=y0-a/gcd(a,b)*t(t为任意整数)也为方程的解
    且b/gcd(a,b),a/gcd(a,b)分别为x,y的解的最小间距,所以x在0~b/gcd(a,b)区间有且仅有一个解,
    同理y在0~a/gcd(a,b)同样有且仅有一个解,这个解即为我们所需求的最小正整数解。

    为什么b/gcd(a,b),a/gcd(a,b)分别为x,y的解的最小间距?
    解:假设c为x的解的最小间距,此时d为y的解的间距,所以x=x0+c*t,y=y0-d*t(x0,y0为一组特解,t为任意整数)
    带入方程得:a*x0+a*c*t+b*y0-b*d*t=n,因为a*x0+b*y0=n,所以a*c*t-b*d*t=0,t不等于0时,a*c=b*d
    因为a,b,c,d都为正整数,所以用最小的c,d,使得等式成立,ac,bd就应该等于a,b的最小公倍数a*b/gcd(a,b),
    所以c=b/gcd(a,b),d就等于a/gcd(a,b)。

    所以,若最后所求解要求x为最小整数,那么x=(x0%(b/gcd(a,b))+b/gcd(a,b))%(b/gcd(a,b))即为x的最小整数解。
    x0%(b/gcd(a,b))使解落到区间-b/gcd(a,b)~b/gcd(a,b),再加上b/gcd(a,b)使解在区间0~2*b/gcd(a,b),
    再模上b/gcd(a,b),则得到最小整数解(注意b/gcd(a,b)为解的最小距离,重要)

    转:
    首先扩展欧几里德主要是用来与求解线性方程相关的问题,所以我们从一个线性方程开始分析。现在假设这个线性方程为a*x+b*y=m,如果这个线性方程有解,那么一定有gcd(a,b) | m,即a,b的最大公约数能够整除m(m%gcd(a,b)==0)。证明很简单,由于a%gcd(a,b)==b%gcd(a,b)==0,所以a*x+b*y肯定能够整除gcd(a,b),如果线性方程成立,那么就可以用m代替a*x+b*y,从而得到上面的结论,利用上面的结论就可以用来判断一个线性方程是否有解。
     
          那么在a*x+b*y=m这个线性方程成立的情况下,如何来求解x和y呢?
     
          1.令a1=a/gcd(a,b),b1=b/gcd(a,b),m1=m/gcd(a,b)。如果我们能够首先求出满足a1*x1+b1*y1=gcd(a1,b1)这个方程的x1和y1,那么x=x1*m1,y=y1*m1就可以求出来了。由欧几里德算法gcd(a,b)=gcd(b,a%b),所以a*x1+b*y1=gcd(a,b)=gcd(b,a%b)=b*x2+(a%b)*y2,现在只要做一些变形就可以得到扩展欧几里德算法中的用到的式子了。令k=a/b(商),r=a%b(余数),那么a=k*b+r。所以r=a-k*b,带入上式,得到a*x1+b*y1=b*x2+(a-(a/b)*b)y2=a*y2+b*(x2-(a/b)*y2) => x1=y2,y1=x2-(a/b)*y2。有了这两个式子我们就知道了在用欧几里德求最大公约数的时候,相应的参数x,y的变化。现在再回过头来看一下扩展欧几里德算法的代码就很好理解了,实际上扩展欧几里德就是在求a和b的最大公约数的同时,也将满足方程a*x1+b*y1=gcd(a,b)的一组x1和y1的值求了出来。下面代码中突出的部分就是标准的欧几里德算法的代码。
     
    __int64 exGcd(__int64 a,__int64 b,__int64 &x,__int64 &y){
        if(b==0){
            x=1;
            y=0;
            return a;
        }
        __int64 g=exGcd(b,a%b,x,y);
        __int64 temp=x;
        x=y;
        y=temp-(a/b)*y;
        return g;
    }
     
         2.那么x,y的一组解就是x1*m1,y1*m1,但是由于满足方程的解无穷多个,在实际的解题中一般都会去求解x或是y的最小正数的值。以求x为例,又该如何求解呢?还是从方程入手,现在的x,y已经满足a*x+b*y=m,那么a*(x+n*b)+b*(y-n*a)=m显然也是成立的。可以得出x+n*b(n=…,-2,-1,0,1,2,…)就是方程的所有x解的集合,由于每一个x都肯定有一个y和其对应,所以在求解x的时候可以不考虑y的取值。取k使得x+k*b>0,x的最小正数值就应该是(x+k*b)%b,但是这个值真的是最小的吗??如果我们将方程最有两边同时除以gcd(a,b),则方程变为a1*x+b1*y=m1,同上面的分析可知,此时的最小值应该为(x+k*b1)%b1,由于b1<=b,所以这个值一定会小于等于之前的值。在实际的求解过程中一般都是用while(x<0)x+=b1来使得为正的条件满足,为了更快的退出循环,可以将b1改为b(b是b1的倍数),并将b乘以一个倍数后再加到x上。
  • 相关阅读:
    SQL Server 2005的通用分页存储过程
    沉默的羔羊 赵传
    [转]Ubuntu 10.04 安装 codeblocks10.051 过程详细解析
    VMware7.1.5虚拟机安装Ubuntu 11.10使用share folders共享目录
    【转】libmemcached在windows下的PHP扩展(php_memcached.dll下载)
    Ubuntu升级之后The disk drive for / is not ready yet or not present的解决方法
    基于libmemcached,php扩展memcached的安装
    ubuntu configure的问题解决
    Ubuntu安装CodeBlocks相关问题(不能编译或者编译通过但不能生成可执行文件)
    ubuntu笔记
  • 原文地址:https://www.cnblogs.com/zhangmingcheng/p/4231016.html
Copyright © 2011-2022 走看看