乘法逆元

Posted on 2025-04-18 23:04  K_J_M  阅读(24)  评论(0)    收藏  举报

现有一个线性同余方程\(ax≡1(\bmod \ p)\)
则称\(x\)\(a\)在模\(p\)意义下的逆元。
那么如何求逆元呢?
我们先求简单一点的情况,
求单个数的逆元。
此方程\(ax≡1(\bmod \ p)\)等价于求满足\(ax+py=1\)
的解。
显然,
\((a,p)=1\)是此方程有解的充分必要条件。
接下来介绍扩展欧几里得算法来求逆元。

扩欧

\[ax≡1(\bmod \ p),(a,p)=1 \]

此方程化为

\[ax+py=1,(a,p)=1 \]

将1变换一下可以得到

\[ax+py=(a,p)=(p,a\bmod p)=px_0+(a\bmod p)y_0 \]

注意到

\[a\bmod p=a-\lfloor\frac{a}{p} \rfloor \times p \]

因此原式又可以化为

\[px_0+(a-\lfloor\frac{a}{p} \rfloor \times p)y_0=1 \]

去括号展开一下

\[px_0+ay_0-\lfloor\frac{a}{p} \rfloor \times p y_0=1 \]

\[p(x_0-\lfloor\frac{a}{p} \rfloor \times y_0)+ay_0=1 \]

观察一下两个方程的系数

\[ax+py=1 \]

\[p(x_0-\lfloor\frac{a}{p} \rfloor \times y_0)+ay_0=1 \]

便可以得到一个惊人的结论,
我们在求解方程\(ay_0+px_0=1\)的时候,
得到的\(x_0,y_0\)可以推出方程\(ax+py=1\)\(x,y\)
也就是说,
我们可以通过计算\(x_0,y_0\)来得到\(x,y\)
然后有注意到,
我们这样子计算下去,
类似于欧几里得求最大公约数的过程,
最终我们会使得\(p\)变成0,
此时的方程为

\[1\times x+0\times y=1 \]

可以得到\(x=1,y=0\)的一组解,
然后可以一直往上求解出\(x,y\)的值。
由于欧几里得算法求最大公约数的时间复杂度为\(O(\log n)\)
所以扩展欧几里得算法的时间复杂度也是\(O(\log n)\)
这样我们便求解出了单个数的逆元。

费马小定理

费马小定理描述如下

\[a^{p-1}≡1(\bmod \ p),p∈Prime,(a,p)=1 \]

证明读者自行查阅资料。
有了这个定理,
求解逆元便很简单了。
将费马小定理的式子变一下形可以得到

\[a\times a^{p-2}≡1(\bmod \ p ) \]

观察可知\(a^{p-2}\)就是\(a\)在模\(p\)意义下的逆元,
求解方法考虑使用快速幂。
时间复杂度\(O(\log p)\)

欧拉定理

欧拉定理描述如下,
首先定义,

\[φ(n) \]

表示\([1,n]\)以内与\(n\)互素的数的个数,
那么欧拉定理,

\[a^{φ(p)}≡1(\bmod \ p ) \]

两边同时乘以\(a^{-1}\)可以得到,

\[a^{φ(p)-1}≡a^{-1}(\bmod \ p ) \]

那么我们就找到了求解逆元的另一种方式,
时间复杂度根据欧拉函数的求解方式计算,
时间复杂度在\(O(\sqrt p)\sim O(\log p)\)不等。
当然,这个不用考虑\(p\)是不是素数的情况。

线性求逆元

刚才讨论到的都只能求单个的逆元,
可是求多个的逆元时时间复杂度又很高,
这时候我们可以使用线性求逆元。
线性求逆元可以解决求\(1\sim n\)的逆元。
我们要求解

\[i^{-1}≡1(\bmod \ p) \]

首先

\[1^{-1}≡1(\bmod \ p) \]

这是很显然的。
我们将原式变一下形,

\[p=\lfloor \frac{p}{i} \rfloor \times i + p\bmod i \]

\[∵p≡0(\bmod \ p ) \]

\[∴\lfloor \frac{p}{i} \rfloor \times i + p\bmod i≡0(\bmod \ p ) \]

两边同时乘上\(i^{-1}\times (p\bmod i)^{-1}\)可以得到

\[\lfloor \frac{p}{i} \rfloor \times (p\bmod i)^{-1}+i^{-1}≡0(\bmod \ p ) \]

\[∴i^{-1}≡-\lfloor \frac{p}{i} \rfloor \times (p\bmod i)^{-1}(\bmod \ p ) \]

由于\(p \bmod i <i\)
所以我们求解\(i\)的逆元时,
可以利用前面求解过的逆元来求解\(i^{-1}\)
这样我们就做到了线性求解逆元,
时间复杂度\(O(n)\)

阶乘求逆元

我们求出

\[(∏_{i=1}^{n}i)^{-1} \]

也就是

\[n!^{-1} \]

记为

\[f_n \]

那么

\[f_i=f_{i+1}\times (i+1) \]

在记

\[inv_i \]

表示\(i\)的逆元,
那么,

\[inv_i=f_i\times (i-1)! \]

这里\(0!=1\)
这样我们就完成了\(O(n+\log p)\)接近线性求逆元了。

前缀积求逆元

上面的线性求逆元只能求出\(1\sim n\)的逆元,而前缀积求逆元可以求出任意\(n\)个数的逆元。具体如下,现在要求求出\(a_i,(1≤i≤n)\)的逆元。

\[f(k)=∏_{i=1}^{k}a_i \]

求出

\[f(n)^{-1} \]

定义

\[inv_i \]

表示\(a_i\)的逆元
那么

\[inv_i=f(i+1)^{-1}\times f(i) \]

因此我们可以先处理出\(f(n)^{-1}\)
然后,
根据上面的递推公式可以得到\(inv_i\)。这样的时间复杂度为\(O(n+\log \ p )\)。适用性很强。

线性筛

还是求解\([1,n]\)的逆元。
我们现将所有\(Prime∈[1,n]\)用线性筛求解出来,
然后对于每一个数\(i∈[1,n]\)

\[i^{-1}=mpf(i)^{-1}\times (\frac{i}{mpf(i)})^{-1} \]

\(mpf(i)\)表示\(i\)的最小质因数,
我们现将所有的素数的逆元求解出来,
然后将每一个数的逆元通过前面计算过的计算出来,
因为\(\frac{i}{mpf(i)}\)是肯定是前面已经计算过的,
这里的时间复杂度大约为

\[O(n+\frac{n\log p}{㏑_n}) \]