• 乘法逆元


    费马小定理 + 快速幂

    求b mod p 的乘法逆元x(bx = 1(mod p))

    1. 应用前提:a 与 p互质,且p为质数

    原因:该算法的理论前提为费马小定理,所以与费马小定理的要求相同

    1. 逆元的用途
      根据上面逆元的表达式,
      bx = 1 (mod p)
      a*bx = a (mod p)
      ax = a / b (mod p)
      也就是说当我们需要计算 a / b (mod p) 的结果只需要计算ax (mod p)的结果即可,从而解决了除法取模的问题

    !!!!!!残留的问题是我们的推导过程中a是不是需要和p互质,需要看一下书

    #include <iostream>
    
    using namespace std;
    
    typedef long long LL;
    
    LL qpow(int a, int b, int p)
    {
        LL res = 1;
        while (b)
        {
            if (b & 1) res = res * a % p;
            a = (LL)a * a % p;
            b >>= 1;
        }
        
        return res;
    }
    int main()
    {
        int n;
        cin >> n;
        while (n --)
        {
            int a, p;
            cin >> a >> p;
            int res = qpow(a, p - 2, p);
            
            /**
             * 此题中规定了p为质数,而费马小定理的条件是a和p互质且p为质数,那么就要求a不能是p的倍数
             * 然后考虑到如果a是p的倍数,那么a%p==0, 那么a^(p - 2)%p也等于0,所以只需要通过res的值是否为0即可判断是否存在逆元
             * 
             * 但是有一种特殊情况,当p为2时候,我们计算的就是任何数的0次幂,结果均是1,即使当a为p的倍数这种非法情况时我们也无法通过res判断出来
             * 
             * 所以不能通过res来判断答案是否存在
             */
            if (a % p) cout << res << endl;
            else cout << "impossible" << endl;
        }
        
        return 0;
    }
    

    扩展欧几里得算法

    适用场景

    求单个数据的逆元且不能满足费马小定理的条件

    原理

    $ aa^{-1} = 1 (mod m) $, 求解a模m的乘法逆元即求解 ax = 1 (mod m) 的一组解,即求解 ax + my = 1 的一组解

    代码实现

    #include <iostream>
    
    using namespace std;
    
    int exgcd(int a, int b, int &x, int &y)
    {
        if (b == 0)
        {
            x = 1, y = 0;
            return a;
        }
        int gcd = exgcd(b, a % b, y, x);
        y -= a / b * x;
        return gcd;
    }
    int main()
    {
        int a, m, x, y;
        cin >> a >> m;
        int t = exgcd(a, m, x, y);
    
        // 有解的条件为1是gcd(a, m)的倍数
        if (t != 1) cout << "impossible" << endl;
        else cout << x << endl;
        
        return 0;
    }
    

    线性递推

    适用场景

    求1到n所有数据的逆元,此时如果对每一个数采用快速幂或扩展欧几里得计算太慢了

    计算原理:

    模m条件下,1的逆元inv[1] = 1,这是下面推算其它情况的前提

    设 $ t = frac{M}{i}, k = M % i $

    可推导出

    -> (t * i + k = 0 (mod M))

    -> (-t * i = k (mod M))

    上式左右两边同时除 (i * k),或者说左右两边同时乘以 (i^{-1} * k^{-1}), 可得到

    (-t * inv[k] = inv[i] (mod M))

    将t 和 k 进行替换可得 (inv[i] = (M - frac{M}{i}) * inv[M \% i] \% M) ,其中使用 (M - frac{M}{i}) 防止出现负数

    计算过程中,如果出现M % i == 0,使用到inv[0]的情况,表示在模M条件下,求i的逆元,其中i与M并不互质,所以 (ix = 1 (mod M)) 是无解的,即此时i不存在逆元。

    代码实现

    #include <iostream>
    
    using namespace std;
    
    const int N = 1e6 + 10;
    
    int n, m;
    int inv[N];
    
    int main()
    {
        cin >> n >> m;
        inv[1] = 1;
        /**
         * 当i == m时,取到inv[0],计算的inv[m] = 0
         * 此后,i>m,所以m%i=m,所以计算得到的inv[i]都等于inv[m]=0
         * 所以难道是我们利用的时候需要考虑这个问题?还是代码有问题?待定
         */
        for (int i = 2; i <= n; ++ i)
            inv[i] = (long long)(m - m / i) * inv[m % i] % m;
        
        for (int i = 2; i <= n; ++ i)
            cout << i << ' ' << inv[i] << endl;
        
        return 0;
    }
    
  • 相关阅读:
    linux命令---常用组合
    linux---进程相关的命令
    linux命令---系统监控
    linux命令---find
    linux命令---sort
    linux命令---tar
    linux命令---split
    linux命令---awk进阶
    log4net使用方法
    URL编码:不同的操作系统、不同的浏览器、不同的网页字符集,将导致完全不同的编码结果。
  • 原文地址:https://www.cnblogs.com/G-H-Y/p/14368195.html
Copyright © 2020-2023  润新知