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

    欧几里德算法(求最大公约数):

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

      

    顺便写下求最小公倍数(lcm)

    LL lcm(LL a, LL b)
    {
        return a*b/gcd(a, b);
    }

    朴素的欧几里德:

      gcd(a, b)  = gcd(b, a%b);

    扩展欧几里德算法:

    LL extend_gcd(LL a, LL b, LL &x, LL &y)
    {
        if(b == 0)
        {
            x = 1;
            y = 0;
            return a;
        }
        else
        {
            LL r = extend_gcd(b, a%b, y, x);
            y -= x*(a/b);
            return r;
        }
    }
    

      

    该算法一般有三种应用:

    1. 求解不定方程: ax + by = c (贝祖等式: ax + by = gcd(a, b) )
    2. 求解模线性方程(线性同余方程)
    3. 求解模的逆元

     

    应用1:

    • 利用它可以求解整数对(x, y).一定存在这样的整数对(x, y), 使得ax + by = gcd(a, b);对于方程ax + by = c 是否有解可以通过判断c是否是gcd(a, b)的倍数, 即gcd(a, b)|c; 如果方程 ax + by = g(令 g = gcd(a, b))的一组解是(x0, y0), 则方程 ax + by = c 有解时, 它的一组解是(x0*c/g, y0*c/g)(gcd(a, b)也表示方程解得个数).
    • 利用欧几里德可以较容易求出 ax + by = gcd(a, b)的一组解(x1, y1), 任取另一组解(x2, y2), 则ax1 + by1 = ax2 + by2,因为它们都等于gcd(a, b);移项并合并同类项得 a(x1-x2) = b(y2-y1), 令 gcd(a, b) = g, 等式两边同除以g得, a'(x1-x2) = b'(y2-y1) (a' = a/g, b' = b/g), 此时a'与b'互素(互素:没有共同的质因子或者可以说最大公约数只有1), 由此可知x1 - x2一定是b'的整数倍, 令x1 - x2 = kb', 则y2 - y1 = ka'.
    • 不定方程的通解和特解

        通解: 通过上面的第二点可知, 设a, b, c为任意整数, 若方程ax + by = c的一组整数解为(x0, y0), 则它的任意整数解都可以写成(x0+kb', y0-ka') (a' = a/gcd(a, b), b' = b/gcd(a, b), k取任意整数);所以(x0+kb', y0-ka')就是不定方程的通解.

        特解:利用extend_gcd()可以求得一组解(x, y), 令 ans = extend_gcd(), 特解就等于c*x/ans.

        最小解:在实际问题当中,需要的往往是最小整数解,可以通过下面的方法求出最小整数解:

          令t = b/gcd(a, b),x是方程a*x + b*y = n的一个特解,则x = c*x/ans, xmin = (x % t + t) % t, y = (c-a*x)/b.

    一道采用扩展欧几里德算法的题: poj 1061--青蛙的约会

    应用2:

    • 模线性方程组: ax ≡ b(mod n) ("≡"为同余号)   含义:"a和b关于模n同余" 或可以说是 "a和b除以n的余数相同"
    • 充要条件:a-b是n的整数倍
    • 根据模线性方程的定义可知:ax = q*n + r, b = q'*n + r'(a,b,n,q,q',r 都为整数) 由于它们的余数相同即 r = r' 联立等式得, ax - b = n*(q - q') (ax - b是n的整数倍) 令 y = q - q'; 则 ax - ny = b (又回到了不定方程).

      

    LL cal(LL a, LL m, LL c)
    {
        LL x, y;
        LL ans = extend_gcd(a, m, x, y);
        if(c%ans==1)
            return -1;
        x *= c/ans;
        m /= ans;
    //    m = abs(m); //abs只能对int取绝对值
        if(m < 0)
            m = m*(-1);
        LL sum = x%m;   //求解最小解
        if(sum <= 0)
            sum += m;   //保证最小解非负
        return sum;
    }

    应用3:

    • 当上面的模线性方程组中的 b为1时, 那么就称 ax ≡ 1(mod n) 的解为 a 关于模n的逆(模的逆元),它是模线性方程组的一种特殊式 (也称 x 是 a 关于 m 的乘法逆元,用来求最小解)
    • 由上面模线性方程组的转化可知 ax - ny = 1,要想该等式有解,必须使得贝组等式 ax - ny = 1且等式只有唯一解
    • 通解:x = x0 + k*(n/gcd(a,n))    最小解:x = x % n  ( gcd(a, n) = 1 ) 其实与上面不定方程求最小解是一样的
    //乘法逆元:ax≡1(mod m)
    //a对于m的乘法逆元模板
    
    LL cal(LL a, LL m)
    {
        LL x, y;
        LL ans = extend_gcd(a, m, x, y);
        if(1%ans==1)    
            return -1;
        x *= 1/ans;
    //    m = abs(m);    //abs只能对int取绝对值
        if(m < 0)
            m = m*(-1);
        LL sum = x%m;
        if(sum <= 0)
            sum += m;
        return sum;
    }

     

  • 相关阅读:
    自定义动画animate()
    【Java】正则表达式
    【Java】连接数据库SQLServer
    【Java】导入导出TXT文件
    【数据库】SELECT语句
    数据结构与算法系列之目录
    【Java】员工统计
    【Java】生产者消费者模式
    【Java】购物超市
    【Java】导入导出Excel表格
  • 原文地址:https://www.cnblogs.com/xl1164191281/p/5719559.html
Copyright © 2011-2022 走看看