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

    扩展欧几里得算法

    为了介绍扩展欧几里得,我们先介绍一下贝祖定理

               即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。

    换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍。(可以来判断一个这样的式子有没有解)

    有一个直接的应用就是 如果ax+by=1有解,那么gcd(a,b)=1;

    要求出这个最大公因数gcd(a,b),我们最容易想到的就是古老悠久而又相当强大的辗转相除法:

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


    但是,对于上面的式子ax+by=m来说,我们并不仅仅想要知道有没有解,而是想要知道在有解的情况下这个解到底是多少。

    所以,扩展欧几里得

            当到达递归边界的时候,b==0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a*1+b*0=gcd(a,b),x=1,y=0,注意这时的a和b已经不是最开始的那个a和b了,所以我们如果想要求出解x和y,就要回到最开始的模样。(倒着递归回去)

            初步想法:由于是递归的算法,如果我们知道了这一层和上一层的关系,一层一层推下去,就可以推到最开始的。类似数学上的数学归纳法。

            假设当前我们在求的时a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得                          b*x1+(a%b)*y1=gcd

    (注意在递归算法中,永远都是先得到下面一个状态的值)

    这时我们可以试着去寻找这两个相邻状态的关系:

    首先我们知道:a%b=a-(a/b)*b;带入:

    b*x1 + (a-(a/b)*b)*y1

    = b*x1 + a*y1 – (a/b)*b*y1

    = a*y1 + b*(x1 – a/b*y1) = gcd   发现 x = y1 , y = y1 – a/b*x

    这样我们就得到了每两个相邻状态的x和y的转化,就可以在求gcd的同时对x和y进行求值了

    代码

    #include<iostream>
    #include<cmath>
    using namespace std;
    int exgcd(int a, int b, int &x, int &y)
    {
        if (b == 0)
        {
            x = 1; y = 0;
            return a;
        }
        int r=exgcd(a, a%b, x, y);
        int temp = y;
        x = y;
        y = x - (a / b)*temp;
        return r;
    
    }

    算法应用

    求解逆元

    逆元的概念:对于缩系中的元素,每个数a均有唯一的与之对应的乘法逆元x,使得ax≡1(mod n)。一个数有逆元的充分必要条件是gcd(a,n)=1,此逆元唯一存在。

    逆元的意义:模n意义下,1个数a如果有逆元x,那么除以a相当于乘以x

    给定模数n,求a的逆相当于求解ax=1(mod n),这个方程可以转化为ax-my=1,然后套用二元一次方程的方法,用扩展欧几里得算法求得一组x0,y0和gcd;

    检查gcd是否为1,gcd不为1说明逆元不存在,若为1,调整x0到0~m-1的范围中即可。

    代码

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    using namespace std;
    long long x, y;
    long long exgcd(long long a, long long b, long long &x, long long &y)
    {
        if (b == 0)
        {
            x = 1; y = 0;
            return a;
        }
        long long r = exgcd(b, a%b, y, x);//因为x变为y,所以xy互换位置
        y -= (a / b)*x;
        return r;
    }
    long long reverse(long long a, long long n)//ax=1(mod n) 求a的逆元x 
    {    
        long long d;
        d = exgcd(a, n, x, y);
        if (d == 1)//若为1,调整x0到0~m-1的范围中即可
            return (x%n + n) % n;
        else return -1;//gcd不为1说明逆元不存在
    }

    原文链接:https://blog.csdn.net/destiny1507/article/details/81750874、https://blog.csdn.net/Greenary/article/details/79343176

  • 相关阅读:
    dp
    数学分析 + 容斥原理
    容斥
    并查集
    矩阵hash + KMP
    扫描线
    位运算
    2015 Multi-University Training Contest 5 1009 MZL's Border
    iOS ZipArchive文件解压缩
    iOS GCD倒计时
  • 原文地址:https://www.cnblogs.com/Jason66661010/p/12939709.html
Copyright © 2011-2022 走看看