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

    1. 扩展欧几里得算法

    扩展欧几里得算法用于求解这样一个问题:给定两个非零整数 (a)(b) ,求一组整数解 ((x,y)) 使得 (ax+by = gcd(a,b)) 成立。

    易知:

    [ax+by = gcd(a,b) = gcd(b,a\%b) = ... = gcd(a',0) = a' \ a'x+0y = gcd(a',0) = a' => x=1 ]

    下面说明求解过程。

    [egin{aligned} gcd(a,b) &= gcd(a,a\%b) \ ax+by &= gcd(a,b) end{aligned} ]

    所以设:

    [egin{aligned} ax_1+by_1 &= gcd(a,b) \ bx_2+(a\%b)y_2 &= gcd(b,a\%b) end{aligned} ]

    因此有:

    [bx_2+(a\%b)y_2 = ax_1+by_1 ]

    又因为(/表示整除):

    [a\%b = a - (a/b)*b ]

    所以:

    [egin{aligned} bx_2+(a - (a/b)*b)y_2 &= ax_1+by_1 \ ay_2+b(x_2 - (a/b)y_2) &= ax_1+by_1 end{aligned} ]

    所以:

    [egin{aligned} x_1 &= y_2 \ y_1 &= x_2 - (a/b)y_2 end{aligned} ]

    重复进行以上步骤,到达递归边界时必然可以得一组解。

    回到原式 (ax+by = gcd(a,b)) ,可得到递归边界:

    when b=0: ax = gcd(a,0) = a 
    so: x=1, y=random
    

    代码实现:

    //solve: ax + by = gcd(a,b)
    int extGcd(int a, int b, int &x, int &y)
    {
        if (b == 0)
        {
            // the equation has more than one solution
            // here, y can be a random val, which decide {x,y} at the last
            x = 1, y = (int)random() % 10;
            return a;
        }
        int gcd = extGcd(b, a % b, x, y);
        int t = y;
        y = x - (a / b) * y;
        x = t;
        return gcd;
    }
    

    下面继续探讨如何得出 ax+by=gcd(a,b) 的所有解。

    先说结论:

    [x' = x_0 + frac{b}{gcd(a,b)}K \ y' = y_0 - frac{a}{gcd(a,b)}K ]

    下面看求解过程。

    设新的解为 (x_0+s_1)(y_0 - s_2) :

    [egin{aligned} a(x_0+s_1)+b(y_0-s_2) &= ax_0 + by_0 \ as_1-bs_2 &= 0 \ frac{s_1}{s_2} &= frac{b}{a} = frac{b/gcd(a,b)}{a/gcd(a,b)} end{aligned} ]

    显然,(frac{b}{gcd(a,b)})(frac{a}{gcd(a,b)}) 是互质的(没有大于 1 的公约数)。

    所以取::

    [s_1 = frac{b}{gcd(a,b)}*K \ s_2 = frac{a}{gcd(a,b)}*K ]

    其中,(K) 是任意的整数。

    那么问题又来了,方程中的 (x) 的最小非负整数解是什么呢?

    从通解式 (x' = x_0 + frac{b}{gcd(a,b)}K) 上看,应当是 (x' \% frac{b}{gcd(a,b)} = x_0 \% frac{b}{gcd(a,b)})

    但是由于在递归边界时,(y) 可以取任意值,所得的特解 (x_0) 可能为负,不能保证 (x_0 \% frac{b}{gcd(a,b)}) 是非负的。

    如果 (x_0 \% frac{b}{gcd(a,b)}) 是负数,那么其取值范围是:

    [(-frac{b}{gcd(a,b)},0) ]

    所以,(x) 的最小非负整数解为:

    [egin{aligned} x_0 \% frac{b}{gcd(a,b)} + frac{b}{gcd(a,b)} quad &if quad x_0<0 \ x_0 \% frac{b}{gcd(a,b)} quad &if quad x_0 ≥ 0 end{aligned} ]

    综合一下,也就是:

    [(x_0 \% frac{b}{gcd(a,b)} + frac{b}{gcd(a,b)}) \% frac{b}{gcd(a,b)} ]

    2. ax+by=c求解

    (ax+by=c) 有解的充要条件是 (c \% gcd(a,b) == 0)

    如果 (ax+by=gcd(a,b)) 有一组解为:((x_0,y_0)),即:

    [ax_0+by_0=gcd(a,b) ]

    两边同乘以 (frac{c}{gcd(a,b)})

    [afrac{cx_0}{gcd(a,b)} + bfrac{cy_0}{gcd(a,b)} = c ]

    所以:

    [(frac{cx_0}{gcd(a,b)},frac{cy_0}{gcd(a,b)}) ]

    是方程 (ax+by=c) 的一个特解。

    同理可得:

    [egin{aligned} & a(x'+s_1)+b(y'-s_2) = c \ & ax'+by'= c \ & frac{s_1}{s_2} = frac{b}{a} = frac{b/gcd(a,b)}{a/gcd(a,b)} end{aligned} ]

    所以,通解为:

    [x = frac{cx_0}{gcd(a,b)} + b/gcd(a,b)*K \ y = frac{cy_0}{gcd(a,b)} - a/gcd(a,b)*K ]

    3. 同余式 ax ≡ c (MOD m) 求解

    首先解释何为「同余式」,给定整数 (a)(b) ,如果说二者「模 (m) 同余」,即满足:(a\%m == b\%m)

    (或者是满足 ((a-b)\%m==0) )。

    显然,每个整数各自与 ([0,m)) 内的唯一整数满足同余关系。

    现在需要求解的问题是:同余式 (ax = c quad (MOD quad m)) 的解。

    由同余式的定义,可得:

    [(ax-c)\%m=0 ]

    那么我们设:

    [egin{aligned} ax-c &= my \ ax - my &= c end{aligned} ]

    (y = -y),可得:

    [ax+my=c ]

    使用上面第二小节的结论可得:

    1. 如果 (c \% gcd(a,m) eq 0) ,则同余式 (ax=c(MODquad m)) 无解。

    2. 如果 (c\%gcd(a,m)=0),那么设 (ax_0+my_0 = gcd(a,m)) ,则有:

    [egin{aligned} x &= frac{cx_0 + m*K}{gcd(a,m)},quad K = 0,1,..., gcd(a,m)-1 end{aligned} ]

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

    本文的最后一个问题:(a,m) 是整数,求 (a)(m) 的逆元。

    首先解释一下,何为「模 (m) 的逆元」:如果 (a,b) 满足:(a*b = 1(MOD quad m)),那么我们就说 (a,b) 互为模 (m) 的逆元。

    易知,逆元还有以下等价定义:

    [egin{aligned} (a*b-1)\%m &= 0 \ a*b\%m &= 1 \ a &= frac{1}{b} (MOD quad m) \ b &= frac{1}{a} (MOD quad m) end{aligned} ]

    那逆元到底有什么用呢?当我们计算 ((a*b)\%m) 时,编程实现时为了防止溢出会计算其等价式:(((a\%m)*(b\%m))\%m) 。但是计算 ((b/a)\%m) 时,该编程方法就无能为力了。

    如果可以找到 (a)(m) 的逆元 (x),即:(a*x=1(MOD quad m)),那么:

    [(b/a)\%m = (b*x)\%m ]

    要求解 (x),实质上就是求解:

    [ax=1(MODquad m) ]

    根据第三小节的结论:

    1. 如果 (1\%gcd(a,m) eq0) ,即 (gcd(a,m) eq1),即 (a,m) 互质,无解。

    2. 如果 (gcd(a,m)=1),那么 (ax equiv 1(MOD quad m)) 在 (0,m) 上有唯一解:

    求解 (ax equiv 1(MOD quad m)),实质上是:

    [egin{aligned} ax - 1 = my quad &Rightarrow quad ax+my=1=gcd(a,m) \ end{aligned} ]

    使用第一小节的扩展欧几里得算法,可得 (x_0) ,最后最小非负数解就是逆元:

    [a^{-1} = (x_0\%m+m)\%m ]

    cpp代码:

    int inverse(int a, int m)
    {
        int x, y;
        int gcd = extGcd(a, m, x, y);
        return (x % m + m) % m;
    }
    

    5. 费马小定理

    费马小定理:

    (m) 是素数 ,且 (a\%m eq 0),那么 (a^{m-1} equiv 1(MOD quad m))

    显然 (a*a^{m-2} equiv 1 (MOD quad m))(a^{m-2}) 就是 (a)(m) 的逆元,可使用快速幂求解。

  • 相关阅读:
    马拉车算法
    n皇后问题(回溯算法)
    求解最大升序子序列问题(动态规划)
    利用二进制进行快速乘法:俄罗斯农名乘法
    Redis、MySQL、Hive、Hbase的区别,数据库和数据仓库的区别
    MySQL数据库
    算法工程师的Bug与Debug
    复习KNN并实现
    文本领域数据增强技术
    Fasttext模型总结
  • 原文地址:https://www.cnblogs.com/sinkinben/p/11638327.html
Copyright © 2011-2022 走看看