• 数学问题——拓展欧几里得算法


    一、拓展欧几里得算法

      该算法用来解决这样一个问题:给定两个非零整数 a 和 b,求一组整数解 (x,y) ,使得 ax + by = gcd(a,b) 成立,其中 gcd(a,b) 表示 a 和 b 的最大公约数。

      • 递归边界:当 b 为 0 时,此时的 a 就等于 gcd,显然有 a*1+b*0=gcd 成立,此时 x=1,y=0;
      • 递推公式:设当计算 gcd(a,b) 时,有 ax1 + by1 = gcd 成立;而在下一步计算gcd(b,a%b) 时,又有 bx2 + (a%b)y2 = gcd 成立。有以下递推式:    

            $ left{egin{matrix}x_{1} = y_{2}\ y_{1} = x_{2} - (a/b)y_{2}end{matrix} ight.  $

      代码如下:

     1 /*    
     2     拓展欧几里得算法 
     3 */
     4 
     5 #include <stdio.h>
     6 #include <string.h>
     7 #include <math.h>
     8 #include <stdlib.h>
     9 #include <time.h>
    10 #include <stdbool.h>
    11 
    12 int x=0, y=0;
    13 // 给定两个非零整数 a 和 b,求一组整数解 (x,y) ,使得 ax + by = gcd(a,b) 成立
    14 int exGcd(int a, int b) {
    15     if(b == 0) {    // 递归边界 
    16         x = 1;
    17         y = 0;
    18         return a;    // 返回最大公约数 
    19     }
    20     int g = exGcd(b, a%b);
    21     int temp = x;    // 存放 x 的值
    22     x = y;            // 递推式 
    23     y = temp - a/b*y; 
    24     return g;        // g 是最大公约数 
    25 }
    26 
    27 int main() {
    28     int gcd = exGcd(4,3);
    29     printf("4*%d + 3*%d = %d
    ", x, y, gcd); 
    30 
    31     return 0;
    32 }

      当 exGcd 函数结束时 x 和 y 就是所求的解。显然,在得到这样一组解之后,就可以通过下面的式子得到全部解

          $left{egin{matrix} x^{'} = x + frac{b}{gcd}*K \  y^{'} = y - frac{a}{gcd}*K end{matrix} ight.$  (K 为任意整数)

      也就是说, x 和 y 的所有解分别以 b/gcd 与 a/gcd 为周期。那么其中 x 的最小非负整数解是什么呢?从直观上来看就是 x%(b/gcd) 。但是当 x 为负数时,x%(b/gcd) 也会得到一个负数,例如 (-15)%4=-3。考虑到即便 x 是负数,x%(b/gcd) 的范围也是在 (-b/gcd,0) ,因此对任意整数来说,$left ( x\%frac{b}{gcd} + frac{b}{gcd} ight ) \% frac{b}{gcd}$ 才是对应的最小非负整数解

    二、方程 ax + by = c 的求解

       ax + by = c 存在解的充要条件是 c%gcd == 0,且一组解 (x,y) 等于 $left ( frac{cx_{0}}{gcd},frac{cy_{0}}{gcd} ight )$。

      在得到这样一组解之后,就可以通过下面的式子得到全部解

          $left{egin{matrix} x^{'} =  frac{cx_{0}}{gcd} + frac{b}{gcd}*K \  y^{'} = frac{cy_{0}}{gcd} - frac{a}{gcd}*K end{matrix} ight.$ (K 为任意整数)

      除此之外,可以得到和上面一样的结论,对任意整数来说,$left ( x\%frac{b}{gcd} + frac{b}{gcd} ight ) \% frac{b}{gcd}$ 是 ax+by=c 中 x 的最小非负整数解,一般来说可以让 x 取 $frac{cx_{0}}{gcd}$,其中 x0 是 ax+by=gcd 的一个解。  

    三、同余式 ax ≡ c(mod m) 的求解

      先解释什么是同余式。对整数 a、b、m 来说,入门 m 整除 a-b(即 (a-b)%m=0),那么就说 a 与 b 模 m 同余,对应的同余式为 a ≡ b(mod m),m 称为同余式的模。显然,每一个整数都各自与 [0,m) 中唯一的整数同余

      此处要解决的就是同余式 ax ≡ c(mod m) 的求解。根据同余式的定义,有 (ax-c)%m=0 成立,因此存在整数 y,使得 ax-c=my 成立。移项并令 y=-y 后即得 ax+my=c 。

      由上面的结论,$left ( x,y ight ) = left ( frac{cx_{0}}{gcdleft ( a,m ight )},frac{cy_{0}}{gcdleft ( a,m ight )} ight )$。

      至于全部解,有以下结论:

      设 a,c,m 是整数,其中 m≥1,则

      •  若 c%gcd(a,m) ≠ 0,则同余式方程 ax ≡ c(mod m) 无解。
      •  若 c%gcd(a,m) = 0,则同余式方程 ax ≡ c(mod m) 恰好有 gcd(a,m) 个模 m 意义下不同的解,且解的形式为      

            $x^{'}=x+frac{m}{gcdleft ( a,m ight )}*K$  

      其中 K = 0,1,... ,gcd(a,m)-1。

       

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

      先解释什么是逆元(此处特指乘法逆元)。假设 a、b、m 是整数,m>1。且有 ab ≡ 1(mod m) 成立,那么就说 a 和 b 互为模 m 的逆元。通俗的说,如果两个数的乘积模 m 后等于 1,就称它们互为 m 的逆元。

      那么逆元有什么用处呢?当要计算 (b/a)%m 时,通过找到 a 模 m 的逆元 x,就有 (b/a)%m = (b*x)%m = ((b%m)*(x%m))%m 成立。这对于解决被除数 b 非常大的问题来说是非常实用的。

      由定义知,求 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) 上有唯一解,直接使用拓展欧几里得算法解出 x 之后就可以用 (x%m + m)%m 得到 (0,m) 范围内的解,也就是所需要的逆元。    

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

      费马小定理:设 m 是素数,a 是任意整数且 a $ otequiv$ 0(mod m),则 am-1 ≡ 1(mod m)。

  • 相关阅读:
    《Java4Android视频教程》学习笔记(二)
    漫画
    MyEclipse启动时报 Unable to acquire application service. Ensure that the org.eclips
    linux下关闭桌面模式使用命令行模式及其它模式
    nginx 开启fastcgi 可支持php、python、perl等多种语言
    linux下安装ImageMagick和Imagick扩展
    nginx 平滑升级到Tengine并编译concat
    centos 安装详解
    for xml path group by
    泛型对象Lists转xml
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes16.html
Copyright © 2020-2023  润新知