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

  • 相关阅读:
    101. Symmetric Tree(js)
    100. Same Tree(js)
    99. Recover Binary Search Tree(js)
    98. Validate Binary Search Tree(js)
    97. Interleaving String(js)
    96. Unique Binary Search Trees(js)
    95. Unique Binary Search Trees II(js)
    94. Binary Tree Inorder Traversal(js)
    93. Restore IP Addresses(js)
    92. Reverse Linked List II(js)
  • 原文地址:https://www.cnblogs.com/Andy-park/p/12088248.html
Copyright © 2011-2022 走看看