乘法逆元学习笔记
前言
在学校科技协会面试的时候被乘法逆元给单杀了,所以来严肃重新学习一下乘法逆元。
定义
对于整数 (a) 和模数 (m),如果存在整数 (x),使得:
$$a x \equiv 1 \pmod m $$
那么称 (x) 是 (a) 在模 (m) 意义下的乘法逆元,记作:
$x\equiv a^{-1}\pmod m$
乘法逆元存在的充要条件是:
$$ gcd(a,m)=1 $$
例如,模 (7) 下,(3) 的逆元是 (5),因为:
$$ 3\times 5=15\equiv 1\pmod 7 $$
乘法逆元常用于模意义下的除法,例如:
$$ \frac{a}{b}\pmod m $$
可以转化为:
$$ a\cdot b^{-1}\pmod m $$
也就是说,模运算中的“除法”通常通过乘以除数的逆元来实现。
费马小定理法
费马小定理:若 $p$ 是质数且 $\text{gcd}(a,p)=1$ ,则 $a^{p-1} \equiv 1 \pmod p$
于是,根据费马小定理,可以推出:
$$a \cdot a^{p-2} \equiv 1 \pmod p$$
回去看看乘法逆元的定义,发现:
$$a^{-1} \equiv a^{p-2} \pmod p$$
所以我们只需要用快速幂求出 $a^{p-2} \bmod p$ 就求出了 $a$ 在模 $p$ 意义下的乘法逆元。
而该方法的条件显然就是 $p$ 是素数且 $p \nmid a$。
1 | long long qpow(long long a, long long e, long long mod) { |
扩展欧几里得法
裴蜀定理:对于任意整数 $a,b$ 存在整数 $x,y$ 使得 $ax+by=\text{gcd}(a,b)$ 。
在计算乘法逆元的时候,如果 $\text{gcd}(a,m)=1$,那么根据裴蜀定理就有:
$$ax+my=1$$
两边同时对 $m$ 取模,有:
$$ax+my \equiv 1 \pmod m$$
因为 $my$ 是 $m$ 的倍数,所以有:
$$my \equiv 0 \pmod m$$
综上,有:
$$ax \equiv 1 \pmod m$$
而扩展欧几里得算法就是用来解决 $ax+by=\text{gcd}(a,b)$ 解出 $x,y$ 的,所以此时解出的 $x$ 就是 $a$ 在模 $m$ 意义下的逆元。
1 | long long exgcd(long long a, long long b, |
线性递推法
线性递推法适用于模数 $p$ 是素数的情况,可以 $O(n)$ 的时间复杂度求出 $1,2,…,n$ 的逆元。
设 $inv[i] \equiv i^{-1} \pmod p$ ,显然 $inv[1]=1$。
带余除法:对于任意的正整数 $p,i$ ,都存在唯一整数商 $q$ 和余数 $r$ ,满足:$p=qi+r,0 \le r<i$。
对于$i>1$,进行带余除法,得到:
$$p=\lfloor\frac{p}{i}\rfloor i+(p\bmod i)$$
可以推出:
$$p \bmod i=p-\lfloor\frac{p}{i}\rfloor$$
在模 $p$ 下:
$$p \bmod i \equiv -\lfloor\frac{p}{i}\rfloor \pmod p$$
两边同时乘上 $i^{-1}$和$(p \bmod i)^{-1}$ ,可得:
$$inv[i] \equiv -\lfloor \frac{p}{i} \rfloor inv[p \bmod i] \pmod p$$
负数取模:$-x \bmod p=(p-(x \bmod p))\bmod p$
所以递推公式就是:
$$inv[i]=p-\lfloor\frac{p}{i}\rfloor inv[p \bmod i] \bmod p$$
1 | inv[i] = (p - 1LL * (p / i) * inv[p % i] % p) % p; |
前缀积法
前缀积法用于一次求出多个数的乘法逆元。核心思想是:先把所有数乘起来,只求一次总乘积的逆元,再倒推每个数的逆元。
设需要求:
$$a_1^{-1},a_2^{-1},\dots,a_n^{-1}\pmod m$$
并且每个 $a_i$ 都与 $m$ 互质。
我们定义前缀积:$pre[i]=\prod_{j=1}^{i} a_{j} \pmod m$,可以用类似前缀和的思想快速预处理。
我们只需要计算一次 $pre[n]$ 的逆元,记为 $s$,然后从后往前倒推:
$$a_i^{-1}=pre[i-1]\times s \pmod m$$
原因是$pre[i-1] \times s=\prod_{j=1}^{i-1} a_j\times\prod_{j=1}^{i-1}a_j^{-1}$,两边约掉前面的因子正好得到$a^{-1}$和后缀逆元。
然后更新$s\leftarrow s\times a_i\pmod m$。
这样就可以处理 $a_{i-1}$ 了。
1 | vector<long long> pre(n + 1), inv(n); |






