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

    欧几里得算法(辗转相除法)

    求两个正整数的最大公约数,时间复杂度 O(logn)。

    欧几里得算法基于下面这个原理:
    设a,b均为正整数,则gcd(a,b)=gcd(b,a%b)。

    证明:设 a = kb + r,其中 k 和 r 分别为 a 除以 b 得到的商和余数。
    则有 r = a - kb 成立。
    设 d 为 a 和 b 的一个公约数,
    那么由 r = a - kb,得 d 也是 r 的一个约数。
    因此 d 是 b 和 r 的一个公约数。
    又由 r = a % b,得 d 为 b 和 a % b 的一个公约数。
    因此 d 既是 a 和 b 的公约数,也是 b 和 a % b的公约数。
    由 d 的任意性,得 a 和 b 的公约数都是 b 和 a % b 的公约数。
    由 a = kb + r,同理可证 b 和 a % b的公约数都是 a 和 b 的公约数。
    因此 a 和 b 的公约数与 b 和 a % b的公约数都是 a 和 b 的公约数。
    即有 gcd(a,b) = gcd(b,a % b)。
    证毕。

    递归的两个关键:
    ① 递归式:gcd(a,b) = gcd(b,a % b) 。
    ② 递归边界:gcd(a,0) = a 。

    于是可得到下面的求解最大公约数的代码:

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

    更简洁的写法:

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

    对于最小公倍数的实现可以通过求解最大公约数来间接的实现,即a,b的最小公倍数lcm(a,b)=a/gcd(a,b)*b

    扩展欧几里得算法

    裴蜀定理:若 a,b 是整数,且 gcd(a,b)=d,那么对于任意的整数 x,y, ax+by 都一定是 d 的倍数,特别地,一定存在整数 x,y,使 ax+by=d 成立。

    证明:设 a>b。
    显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
    当ab!=0 时
    设 ax1+by1=gcd(a,b);
    bx2+(a mod b)y2=gcd(b,a mod b);
    根据朴素的欧几里德原理有 gcd(a,b)=gcd(b,a mod b);
    则:ax1+by1=bx2+(a mod b)y2;
    即:ax1+by1=bx2+(a-(a/b)b)y2=ay2+bx2-(a/b)by2;
    根据恒等定理得:x1=y2; y1=x2-(a/b)*y2;
    这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.

    扩展欧几里得算法可以在 O(logn) 的时间复杂度内求出系数 x, y。

    int exgcd(int a, int b, int &x, int &y){
        if (b == 0){
            x = 1;
            y = 0;
            return a;
        }
        int g = exgcd(b, a % b, x, y);
        int temp = x;    //存放x的值
        x = y;    //更新x = y(old)
        y = temp - a / b * y;    //更新y = x(old) - a / b * y(old)
        return g;
    }
    

    应用

    1. 方程ax+by=c的求解

    已知:a * x+b * y=gcd

    解:
    a * x / gcd + b * y / gcd = 1

    a * c * x / gcd + b * c * y / gcd = c

    利用当前的x,y构造出新的X,Y
    令X = x * c / gcd Y = y * c / gcd
    则a * X + b * Y = c
    因为此时上面的表达式是新构造出来的
    为了使该表达式有解,必须满足 c%gcd==0

    又因为 X = x0 + k * b / gcd

    令B=b/gcd
    则x0=X%B,y0与其类似

    此时解出来的X,Y即,一对特解
    而x0,y0为最小正整数解。

    int cal(int a,int b,int c){
    	int x,y;
    	int gcd=exgcd(a,b,x,y);
    	if(c%gcd) return -1;
    	x*=c/gcd;
    	b/=gcd;
    	if(b<0)  b=-b;
    	int ans=x%b;
    	while(ans<=0) ans+=b;
    	return ans;
    }
    

    重点

    x*=c/gcd;
    b/=gcd;


    2. 同余式ax ≡ c(mod m)的求解

      先解释什么是同余式。对于整数a、b、m来说,如果m整除a-b(即(a-b)%m=0),那么就说a与b模m同余,对应的同余式为ax ≡ b(mod m),m称为同余式的模。例如10与13模3同余,10也与1模3同余,它们分别记为10 ≡ 13(mod 3)、10 ≡ 1(mod 3)。显然,每一个整数都各自与[0,m)中唯一的整数同余。
      根据同余式的定义,有(ax-c)%m=0成立,因此存在整数y,使得ax-c=my成立。移项并令y=-y后即得ax+my=c。
    模  板上同。

    2. 逆元的求解以及(b/a)%m的计算

      先解释下什么是逆元(此处特指乘法逆元)。假设a、b、m是整数,m>1,且有ab ≡ 1(mod m)c成立,那么就说a和b互为模m的逆元,一般也记作a≡(frac{1}{b})(mod m)或b≡(frac{1}{a})(mod m)。通俗的说,如果两个整数的乘积模m后等于1,就称为它们互为m的逆元。
      那么逆元有什么用处呢?当要计算 (b/a)%m 时,通过找到 a 模 m 的逆元 x,就有 (b/a)%m = (b* x)%m = ((b%m)* (x%m))%m 成立。
      由定义知,求 a 模 m 的逆元,就是求解同余式 ax ≡ 1(mod m),并且在实际使用中,一般把 x 的最小正整数解称为 a 模 m 的逆元

    • 如果 gcd(a,m) ≠ 1,那么同余式 ax ≡ 1(mod m) 无解,a 不存在模 m 的逆元
    • 如果 gcd(a,m) ≠ 1,那么同余式 ax ≡ 1(mod m) 在 (0,m) 上有唯一解,可以通过求解ax+my=1得到。注意:由于gcd(a,m)=1,因此ax+my=1=gcd(a,m),直接使用拓展欧几里得算法解出 x 之后就可以用 (x%m + m)%m 得到 (0,m) 范围内的解,也就是所需要的逆元。
    int inverse(int a,int b){  //求解a模m的逆元
        int x,y;
        int g=exgcd(a,m,x,y);  //求解ax+my=1
        return (x%m+m)%m;  
    }
    

      另外,如果 m 是素数,且 a 不是 m 的倍数,则还可以直接使用费马小定理来得到逆元。

    费马小定理:设 m 是素数,a 是任意整数且 (a ≢ 0(mod m)),则 (a^{m-1} ≡ 1(mod m))

  • 相关阅读:
    Linux常用命令-centos
    USACO 2006 Open, Problem. The Country Fair 动态规划
    USACO 2007 March Contest, Silver Problem 1. Cow Traffic
    USACO 2007 December Contest, Silver Problem 2. Building Roads Kruskal最小生成树算法
    USACO 2015 February Contest, Silver Problem 3. Superbull Prim最小生成树算法
    LG-P2804 神秘数字/LG-P1196 火柴排队 归并排序, 逆序对
    数据结构 并查集
    浴谷国庆集训 对拍
    1999 NOIP 回文数
    2010 NOIP 普及组 第3题 导弹拦截
  • 原文地址:https://www.cnblogs.com/transmigration-zhou/p/12302087.html
Copyright © 2011-2022 走看看