乘法逆元

费马小定理 + 快速幂

求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;
}
posted @ 2021-02-03 17:33  0x7F  阅读(100)  评论(0编辑  收藏  举报