欧几里得&&扩展欧几里得

欧几里得算法:

  欧几里得算法,也叫辗转相除,简称 gcd,用于计算两个整数的最大公约数

  原理:辗转相除法求解最大公约数(公因子)

  设两数为a、b(a>b),用gcd(a,b)表示a,b的最大公约数

  r=a (mod b) ()为a除以b的余数,q为a除以b的商,即a÷b=q.......r。辗转相除法即是要证明gcd(a,b)=gcd(b,r)。

证明: a/b = q;   a = q*b+r;  

      <1>当 r=0时,b为a与b的最大公约数

      <2>当 r!=0时, 设a、b的最大公约数为c, a = m*c; b=n*c;  (因为c为a、b的最大公约数,所以m,n互质)

          r = a-q*b;   r = (m*c)-q*b(n*c)=(m-q*b*n)*c,由此可知c也是r的约数,

         所以我们可以继续b/r直到r为0,我们就得出a,b的最大公约数了

递归:

#include<iostream>
using namespace std;
int gcd(int a,int b){
    if(b==0)
        return a;
    return 
        gcd(b,a%b);
}
int main()
{
    int a,b,ans;
    cin>>a>>b;
    ans = gcd(a,b);
    cout<<ans;
    return 0;
} 
View Code

优化:

#include<iostream>
using namespace std;
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int main()
{
    int a,b,ans;
    cin>>a>>b;
    ans = gcd(a,b);
    cout<<ans;
    return 0;
} 
View Code

扩展欧几里得算法:

  扩展欧几里得算法,简称 exgcd,一般用来求解不定方程,求解线性同余方程,求解模的逆元等

   扩展欧几里得算法:求a*x+b*y=c的通解。

裴蜀定理:对于整数a,b,他们关于x,y的线性不定方程ax+by=dax+by=d,设gcd(a,b)=ggcd(a,b)=g,

          则可证明g|dg|d,换句话说,就是g是a,b的最小线性组合

证明:

.                设a*x+b*y=t,

          <1>当b=0时,t=a(因为gcd算法,if(b==0) return a;),则有a*x=a,易得x=1.

          <2>当b!=0时,设a*x1+b*y1=gcd(a,b),b*x2+(a%b)*y2=gcd(b,a%b);由于gcd(a,b)=gcd(b,a%b),联立有:a*x1+b*y1=b*x2+(a%b)*y2。

              a%b=a-(a/b)*b;(a/b向下取整)。

                             则 ax1+by1=bx2+(a-a/b*b)y2

             ax1+by1=bx2+ay2-a/b*by2

             ax1+by1=ay2+bx2-b*a/b*y2

             ax1+by1=ay2+b(x2-a/b*y2)

             解得 x1=y2 , y1=x2-a/b*y2

根据上面的证明,在实现的时候采用递归做法,先递归进入下一层,等到到达最后一层即 b=0 时就返回x=1 , y=0

再根据 x=y’ , y=x’-a/b/y’ ( x’ 与 y’ 为下一层的 x 与 y ) 得到当层的解。不断算出当层的解并返回,最终返回至第一层,得到原解

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;
}
View Code

拓展欧几里得求逆元

 PS:  a ≡ b (mod m) 读作a同余于b模m,或读作a与b关于模m同余。 比如 26 ≡ 14 (mod 12)。

什么是逆元?ax1 (mod m)   ,这里x就是a的逆元。逆元有什么用呢?如果我们要求a/mod m的值,而a,b很大,设b的逆元为x,

这个时候注意到(a/b)xb=(a/bmod m∗ mod m巧妙地把出发转换成了乘法。

为什没求逆元跟欧几里得算法联系起来了呢?根据上面裴蜀定理,我们知道gcd是a,b两个数线性组合的最小值,其他组合值都是gcd的倍数,

当gcd为1时,a,b互质,满足ax+by=1ax+by=1,移项得ax=by+1,即axmod ,此时的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;
}
View Code

这里1%gcd是看gcd是不是1,前面说了,d(这里是1)应该是gcd的倍数,而且不互质的两个数没有逆元。

x=1/gcdx∗=1/gcd实际上更一般的写为x=d/gcdx∗=d/gcd,也就是求解一般不定方程ax+by=dax+by=d的解,因为d是gcd的倍数,我们就把倍数乘上去解得x=x(d/gcd)x′=x∗(d/gcd)。

m是负数的话,我们取|m|,如果求出来x是负数,就x%|m|,结果再加上|m|即可。(因为有无穷个解,通解为x+mtx+m∗t)

     

猜你喜欢

转载自www.cnblogs.com/Lemon1234/p/11627652.html