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

    参考文献:1. http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html

                       2 . https://www.cnblogs.com/hadilo/p/5914302.html

    一、欧几里得算法(重点是证明,对后续知识有用)

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

      定义 gcd(a,b) 为整数 a 与 b 的最大公约数

      引理:gcd(a,b)=gcd(b,a%b)

      证明:

        设 r=a%b , c=gcd(a,b)

        则 a=xc , b=yc , 其中x , y互质

        r=a%b=a-pb=xc-pyc=(x-py)c

        而b=yc

        可知:y 与 x-py 互质

        证明:

                    假设 y 与 x-py 不互质

                    设 y=nk , x-py=mk , 且 k>1 (因为互质)

                    将 y 带入可得

                    x-pnk=mk

                    x=(pn+m)k

                    则 a=xc=(pn+m)kc , b=yc=nkc

                    那么此时 a 与 b 的最大公约数为 kc 不为 k

                    与原命题矛盾,则 y 与 x-py 互质

        因为 y 与 x-py 互质,所以 r 与 b 的最大公约数为 c

        即 gcd(b,r)=c=gcd(a,b)

        得证

      当a%b=0时,gcd(a,b)=b

     递归算法:

    int gcd(int a,int b)
    {
        if(b==0)
            return a;
        return 
            gcd(b,a%b);
    }
    View Code

    优化:

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

    二、扩展欧几里得算法

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

      引理:存在 x , y 使得 gcd(a,b)=ax+by

      证明:

             当 b=0 时,gcd(a,b)=a,此时 x=1 , y=0

             当 b!=0 时,

             设 ax1+by1=gcd(a,b)=gcd(b,a%b)=bx2+(a%b)y2

             又因 a%b=a-a/b*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 , y 为最后一组解

        而每一组的解可根据后一组得到

        所以第一组的解 x , y 必然存在

        得证

      根据上面的证明,在实现的时候采用递归做法

      先递归进入下一层,等到到达最后一层即 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

    扩展欧几里德算法的应用主要有以下三方面:

    (1)求解不定方程;

    (2)求解模线性方程(线性同余方程);

    (3)求解模的逆元;

    ( 1 )exgcd 解不定方程(使用不将a与b转为互质的方法)

      对于 ax+by=c 的不定方程,设 r=gcd(a,b)

      当 c%r!=0 时无整数解

      当 c%r=0 时,将方程右边 *r/c 后转换为 ax+by=r 的形式

      可以根据扩展欧几里得算法求得一组整数解 x0 , y0

      而这只是转换后的方程的解,原方程的一组解应再 *c/r 转变回去

      (如 2x+4y=4 转换为 2x+4y=2 后应再将解得的 x , y 乘上2)

      则原方程解为 x1=x0*c/r , y1=x0*c/r

      通解 x=x1+b/r*t , y=y1-a/r*t ,其中 t 为整数

      证明:

        将 x , y 带入方程得

        ax+ab/r*t+by-ab/r*t=c

        ax+by=c

        此等式恒成立

        得证

      这里 b/r 与 a/r 为最小的系数,所以求得的解是最多最全面的

      证明:

        为了推出证明中的 ax+by=c ,且想达到更小的系数,只能将 b/r 与 a/r 同除以一个数 s

        而 b/r 与 a/r 互质,且 s 为整数,则 s=1 ,不影响通解

        那么 b/r 与 a/r 就为最小的系数

        得证

    bool linear_equation(int a,int b,int c,int &x,int &y)
    {
        int d=exgcd(a,b,x,y);
        if(c%d)
            return false;
        int k=c/d;
        x*=k; y*=k;    //求得的只是其中一组解
        return true;
    }
    View Code

       附加一道题的代码:

     #include<stdio.h>
     int a,b,c,x,y;
     int exgcd(int a,int b,int &x,int &y)
     {
         if(!b)
         {
             x=1;
             y=0;
             return a;
         }
         int e=exgcd(b,a%b,x,y);
         int temp=x;
         x=y;
         y=temp-a/b*y;
         return e;
     }
     int main()
     {
         scanf("%d%d%d",&a,&b,&c);
         int k=exgcd(a,b,x,y);
         if(c%k)
            printf("Impossible
    ");
         else
         {
             k=c/k;
             x*=k;
             y*=k;
             printf("x=%d,y=%d
    ",x,y);
         }
         return 0;
     }
    View Code

     解出来的解后可以转化为最小整数解:

    x=(x%b+b)%b;(求出的就是最小正整数解);

    套用exgcd模板求得的是一组特殊解,但其实这一个方程式是有一个解系,在很多问题中是要你求得最小整数解,下面我们就解决这个问题,在阅读过很多博客加上自己的理解总结了两种方法(其实差距不大)

    1、a*x+b*y=gcd(a,b)

    void exgcd(int a,int b,int &x,int &y)
    {
        if(b==0)
        {
            x=1;
            y=0;
            return;
        }
        int x1,y1;
        exgcd(b,a%b,x1,y1);
        x=y1;
        y=x1-(a/b)*y1;
    }
    x=(x%b+b)%b;(求出的就是最小正整数解)
    2.可以说这是求最小正整数的模板
    LL e_gcd(LL a,LL b,LL &x,LL &y)
    {
        if(b==0)
        {
            x=1;
            y=0;
            return a;
        }
        LL ans=e_gcd(b,a%b,x,y);
        LL temp=x;
        x=y;
        y=temp-a/b*y;
        return ans;
    }
    
    LL cal(LL a,LL b,LL c)
    {
        LL x,y;
        LL gcd=e_gcd(a,b,x,y);
        if(c%gcd!=0) return -1;
       LL b_=b/gcd;
       x=(x*(c/gcd)%b_)%b_+b_)%b_;
       return x;
    }
    View Code
    这两种本质上没啥区别,只是在一些问题中a,b等系数可能为负,第一种需要预处理,而第二种则可以直接用
    附上学习代码处:https://blog.csdn.net/tianyuhang123/article/details/52102686

    还会往下拓展。。。。尽情期待!

  • 相关阅读:
    解决VS Code调试.NET Core应用遇到的坑
    .NET Core第三方开源Web框架YOYOFx
    【干货】”首个“ .NET Core 验证码组件
    .NET Core爬坑记 1.0 项目文件
    细说ASP.NET Core与OWIN的关系
    dock基本使用
    在VS中使用TinyFox调试OWIN应用(转)
    正则表达式的匹配
    构建乘积数组
    数组中重复的数字
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/8954788.html
Copyright © 2011-2022 走看看