zoukankan      html  css  js  c++  java
  • 乘法逆元

    前置芝士

    欧几里得算法

    欧几里得算法又称辗转相除法,用于计算两个正整数的最大公约数。

    定理

    (gcd(a,b)=gcd(b,a \% b)) (()(a>b)(r=a \% b,r) 不为 (0))

    证明

    (a=kb+r) ((a,b,k,r) 皆为正整数,且 (r<b)),则 (r=a \% b)

    假设 (d)(a,b) 的一个公约数,记作 (d|a,d|b)

    (r=a-kb),两边同时除以 (d)(frac{r}{d}=frac{a}{d}-frac{kb}{d}=m),由等式右边可知 (m) 为整数,因此 (d|r)

    因此 (d) 也是 (b,a \% b) 的公约数。

    假设 (d)(b,a \% b) 的公约数, 则 (d|b,d|(a-k*b),k) 是一个整数。

    进而 (d|a)。因此 (d) 也是 (a,b) 的公约数。

    因此 ((a,b))((b,a \% b)) 的公约数是一样的,其最大公约数也必然相等,得证。

    代码

    最终的 (a) 即为原 (a,b) 的最大公约数。

    int gcd(int x, int y)
    {
        int r;
        while(y != 0) r = x % y, a = y, x = r;
        return x;
    }
    

    或写成递归形式

    int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
    

    裴蜀定理

    定理

    (ax + by = c),则 (gcd(a,b)|c)

    证明

    (s=gcd(a,b)),则 (s|a)(s|b),所以 (s|(ax+by))

    (s|c)。得证。

    代码

    「 洛谷 P4549 【模板】裴蜀定理 」

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
     
    int n, a, ans = 0;
    int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
     
    int main()
     {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%d", &a), ans = gcd(ans, abs(a));
        printf("%d", ans);
        return 0;
    }
    

    扩展欧几里得算法

    定义

    用于求 (ax+by=gcd(a,b)) 的一组解 (x,y)

    解法

    根据欧几里得算法,(gcd(a,b)=gcd(b,a \% b))。即求 (ax+by=bx_0+(a \% b)y_0)(其中 (x_0,y_0)已知)。

    (a \% b=a-b*(a / b))(a / b) 表示 (lfloor frac{a}{b} floor))。

    原式转化为:(ax+by=bx_0+(a-b*(a / b))y_0)

    (ax+by=bx_0+ay_0-b*(a / b)y_0)

    (ax+by=ay_0+b(x_0-(a / b)y_0))

    于是得出 (x,y) 的一组可行解:(x=y_0,y=x_0-(a / b)y_0)。递归求解。

    (b_n=0) 时,(gcd(a_n,b_n)=a_n),取 (x_n=1) 时等式必然成立。

    例题

    「 洛谷 P1082 同余方程 」

    原式 (ax≡1(\% b)) 等同于求 (ax+by=1) 中最小的 (x)(y) 作为辅助答案)。

    而根据裴蜀定理,可知 (gcd(a,b)|1),即 (gcd(a,b)=1)。所以该式有解的条件是 (a,b) 互质。

    原式与 (ax+by=gcd(a,b)) 等价,满足扩展欧几里得算法,可以求出一组通解 (x,y)

    题目要求 (x) 最小,显然 (x) 不断 (+b)(-b) 不会对答案产生影响,所以输出 (((x \% b)+b) \% b) 的值。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    using namespace std;
     
    int a, b, x, y;
     
    void exgcd(int a, int b)
    {
        if(b == 0)
        {
            x = 1, y = 0;
            return ;
        }
        exgcd(b, a % b); 
        int tx = x;
        x = y, y = tx - (a / b) * y;
    }
     
    int main()
    {
        scanf("%d%d", &a, &b);
        exgcd(a, b);
        printf("%d", (x % b + b) % b);
        return 0;
    }
    

    乘法逆元

    定义

    (ab≡1( \% p)) ,称 (b)(a) 关于 (1)(p) 的逆元,记做 (inv(a))

    求解

    费马小定理

    费马小定理:当有两数 (a,p) 满足 (gcd(a,p)=1),有 (a^p≡a( \% p))

    两边同除以 (a)(a^{p-1}≡1( \% p)),即 (a*a^{p-2}≡1( \% p))。用快速幂求出 (a^{p-2}) 即可。

    代码

    int poww(int b, int p, int k)
    {
        int ans = 1;
        while(p)
        {
            if(p & 1) ans = (ans * b) % k;
            b = (b * b) % k;
            p >>= 1;
        }
        return ans;
    }
     
    int main()
    {
        int a, p, inv_a;
        scanf("%d%d", &a, &p);
        inv_a = poww(a, p - 2, p);
    }
    

    扩展欧几里得算法

    参照上面 「 例题 」 的解法求解。

    递推求阶乘逆元

    (inv_{i}=frac{1}{i!}( \% p))

    (inv_{i+1}*(i+1)=frac{1}{(i+1)!}*(i+1)=frac{1}{i!}=inv_i)

    可以先用费马小定理求出 (inv_n) 的值,再倒着递推。

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
     
    int max_num, inv[233333], f[2333333], mod = 1e9+7;
     
    int poww(int b, int p, int k)
    {
        int ans = 1;
        while(p)
        {
            if(p & 1) ans = (ans * b) % k;
            b = (b * b) % k;
            p >>= 1;
        }
        return ans;
    }
     
    int main()
    {
        scanf("%d", &max_num);
        f[0] = 1;
        for(int i = 1; i <= max_num; i++) f[i] = (f[i - 1] * i) % mod;
        inv[max_num] = poww(f[max_num], mod - 2, mod);
        inv[0] = 1;
        for(int i = max_num - 1; i > 0; i--)
            inv[i] = (inv[i + 1] * (i + 1) % mod) % mod;
        return 0;
    }
    

    递推求逆元

    (t=⌊frac{i}{p}⌋,k=p \% i)

    (t*i+k≡0(\% p))

    变形 (-t*i≡k(\% p))

    两边同时除以 (ik)(-t*frac{1}{k}≡frac{1}{i})

    (inv_i≡-t*inv_k)

    所以 (inv_i=(p-⌊frac{i}{p}⌋)*inv_{p \% i} \% p)

    参考文献

    https://blog.csdn.net/leader_one/article/details/75222771

    https://blog.csdn.net/qq_37656398/article/details/81434277

    https://www.luogu.com.cn/blog/wlz123/solution-p4549

    https://www.luogu.com.cn/problemnew/solution/P3811

  • 相关阅读:
    验证信息json返回,前台接收显示无刷新。笔记
    ThinkPHP5 查询数据并处理结果
    PHP 数组和字符串互相转换实现方法
    php+jquery+ajax+json的一个最简单实例
    is_numeric 检测变量是否为数字或数字字符串
    PHP中使用cURL实现Get和Post请求的方法
    mb_substr实例
    推荐:多目标 mmoe
    多目标模型--DBMTL
    正负例优化
  • 原文地址:https://www.cnblogs.com/Andy-park/p/12088248.html
Copyright © 2011-2022 走看看