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


     转:https://www.cnblogs.com/zhanhonhao/p/11329772.html

    欧几里得算法:

      欧几里得算法,也叫辗转相除,简称 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

     最小公倍数lcm: a=m*c b=n*c;m与n互质,所以lcm(a,b)=a*b/c 

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

    扩展欧几里得算法:

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

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

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

              则可证明g|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

    乘法逆元:

        在同余问题中,有ax≡1(mod p),其中p∈{素数},则称x为a在mod p域下的逆。

    拓展欧几里得求逆元

     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=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)

     PS:

         模运算:( a + b ) mod p = ( a mod p + b mod p ) mod p

                             ( a * b ) mod p = ( (a mod p) * (b mod p) ) mod p

     

  • 相关阅读:
    会跳舞的树(只用HTML+CSS)(转)
    国内UED收录
    HDU 1078 dfs+dp
    HDU 1278
    HDU 4499
    HDU 4597
    POJ2777
    POJ1780 Code
    简单的Fleury算法模板
    POJ 2513 无向欧拉通路+字典树+并查集
  • 原文地址:https://www.cnblogs.com/Lemon1234/p/11627652.html
Copyright © 2020-2023  润新知