9乘法逆元

乘法逆元

定义

如果一个线性同余方程$ax\equiv 1\pmod b \(,则\)x\(称为\)a\mod b\(的逆元,记作\)a^{-1}$。

快速幂法

浅浅证明一下:

由定义可得\(ax \equiv 1 \pmod b\);

要根据费马小定理可得 \(a^{b-1}\equiv 1 \pmod b\)

联立可得 \(x \equiv a^{b-2}\pmod b\)

ll qpow(ll a, ll b, int mod)
{
    ll ans = 1;
    a = (a % mod + mod) % mod;
    do
    {
        if (b & 1)
            ans = (a * ans) % mod;
        a = (a * a) % mod;
    }while (b >>= 1);
    return ans;
}

扩展欧几里得法

证明:

由扩展欧几里得可得:

\(ax+by=gcd(a,b)\)

若\(gcd(a,b)=1\),则\(ax+by=1\)

同时取模,可得\(ax\equiv 1 \pmod b\)。

void exgcd(int a, int b, int &x,int &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
    }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}

线性求逆元

求出1,2,......,n中每个数关于p的逆元。

如果对于每个数进行单次求解,以上两种方法就显得慢了,很有可能超时,所以下面讲一讲如何线性(\(O(n)\))求逆元。

对于\(\forall p \in Z\),由\(1 * 1 \equiv 1 \pmod p\)恒成立,故在\(p\)下1的逆元是1,而这是推算出其他情况的基础。

其次对于递归情况$ i^{-1}$,我们令

\(k = \lfloor \frac{p}{i} \rfloor,j = p \bmod i\),有 \(p = ki + j\)。再放到 \(\mod p\) 意义下就会得到:\(ki+j \equiv 0 \pmod p\);

两边同时乘 \(i^{-1} \times j^{-1}\):

\(kj^{-1}+i^{-1} \equiv 0 \pmod p\)

\(i^{-1} \equiv -kj^{-1} \pmod p\)

再带入$ j = p \bmod i$,有 \(p = ki + j\),有:

\(i^{-1} \equiv -\lfloor\frac{p}{i}\rfloor (p \bmod i)^{-1} \pmod p\)

我们注意到$ p \bmod i < i\(,而在迭代中我们完全可以假设我们已经知道了所有的模 p 下的逆元\) j^{-1}, j < i$。

故我们就可以推出逆元,利用递归的形式,而使用迭代实现:

\[i^{-1} \equiv \begin{cases} 1, & \text{if } i = 1, \\ -\lfloor\frac{p}{i}\rfloor (p \bmod i)^{-1}, & \text{otherwise}. \end{cases} \pmod p \]

inv[1] = 1;
for (int i=2; i<=n; i++)
{
    inv[i] = (long long)(p - p / i) * inv[p % i] % p;
}

使用 \(p-\lfloor \dfrac{p}{i} \rfloor\) 来防止出现负数。

线性求任意n个数的逆元

如果需要求任意给定 n 个数\((1 \le a_i < p)\)的逆元,就需要下面的方法:

首先计算 n 个数的前缀积,记为$ s_i$,然后使用快速幂或扩展欧几里得法计算 \(s_n\) 的逆元,记为$ sv_n$。

因为$ sv_n$ 是 n 个数的积的逆元,所以当我们把它乘上 a_n 时,就会和 a_n 的逆元抵消,于是就得到了\(a_1\) 到 $a_{n-1} \(的积逆元,记为\) sv_{n-1}$。

同理我们可以依次计算出所有的 \(sv_i\),于是
$a_i^{-1} \(就可以用\) s_{i-1} \times sv_i $求得。

所以我们就在 $O(n + \log p) $的时间内计算出了 n 个数的逆元。

s[0] = 1;
for(int i=1; i <= n; i++)
{
    s[i] = s[i-1] * a[i] % p;
}
sv[n] = qpow(s[n], p-2);

for(int i = n; i >= 1 ; i--)
{
    sv[i - 1]  = sv[i] * a[i] % p;
}
for(int i=1; i <= n; i++)
{
    inv[i] = sv[i] * s[i-1] % p;
}
posted @ 2025-07-18 19:50  星空丶star  阅读(29)  评论(0)    收藏  举报