乘法逆元入门略解

· · 个人记录

乘法逆元入门略解

1. 定义

对于正整数 a,如果存在 a\times a'\equiv 1(\bmod\ p),那么 a' 就是 a 在模 p 意义下的逆元。这里只考虑 p 为质数,情况简单。

推广一下,逆元就是**取消另一给定元素运算的元素**。例如加法逆元是这个数的相反数。 ### 2. 性质 #### 2.1 存在唯一性 对于正整数 $a$,模 $p$ 之后有且仅有一个逆元。 证明唯一性: 假设 $a$ 有两个不相等的逆元 $a',a''(a'<a'')$,那么: $$ a\times a'\equiv a\times a''\equiv1(\bmod\ p) $$ 设 $k=a''-a'$,那么: $$ a\times(a''-k)\equiv a\times a''(\bmod\ p)$$ $$a\times a''-a\times k\equiv a\times a''(\bmod\ p)$$ $$ a\times k\equiv0(\bmod\ p) $$ $\because a\not=0\therefore k\equiv 0(\bmod\ p)$,即 $a'\equiv a''(\bmod\ p)$,与假设矛盾,原命题成立。 #### 2.2 完全积性函数 对于正整数 $a,b$,$inv(a)\times inv(b)\equiv inv(ab)$。 证明: $$ \because a\times inv(a)\equiv b\times inv(b)\equiv 1(\bmod\ p)$$ $$\therefore a\times inv(a)\times b\times inv(b)\equiv 1\times 1(\bmod\ p)$$ $$\therefore ab\times inv(a)\times inv(b)\equiv1(\bmod\ p)$$ $$\because ab\times inv(ab)\equiv 1(\bmod\ p)\small\text{,且根据逆元的存在唯一性}$$ $$\therefore inv(a)\times inv(b)\equiv inv(ab) $$ #### 2.3 分数取模 对于正整数 $a,b$,$a\times inv(b)\equiv \dfrac{a}{b}(\bmod\ p)$。 证明: $$ \because b\times inv(b)\equiv1(\bmod\ p)$$ $$\therefore b\times inv(b)\times a\equiv a(\bmod\ p)$$ $$\therefore a\times inv(b)\equiv \dfrac{a}{b}(\bmod\ p) $$ 也就是说,$\dfrac{a}{b} \bmod p$ 的值,可以用逆元求出。 $$ \dfrac{a}{b}\equiv a\times inv(b)(\bmod\ p) $$ ### 3. 解法 #### 3.1 费马小定理 前置知识:**费马小定理** > 若 $a$ 为正整数,$p$ 为质数,且 $a\perp p$,那么 $a^{p-1}\equiv 1(\bmod\ p)$。 那么 $a\times a^{p-2}\equiv 1(\bmod\ p)$。 根据乘法逆元的唯一存在性,$inv(a)=a^{p-2}$。快速幂求出即可。 时间复杂度 $O(\log p)$。参考代码为分数取模。 ```cpp int qpow(int base,int k) { int res=1; while(k) { if(k&1) res=1ll*res*base%MOD; k>>=1; base=1ll*base*base%MOD; } return res; } int main() { a=read();b=read(); cout<<1ll*a*qpow(b,MOD-2)%MOD<<endl; return 0; } ``` #### 3.2 扩展欧几里得 找 $a\times inv(a) \equiv 1(\bmod p)$ 的 $inv(a)$,写成方程的形式来看看。 设 $x=inv(a)$。 $$ a\times x\bmod p=1\bmod p$$ $$(a\times x-1) \bmod p=0 $$ 即,$p|(ax-1)$,设 $(ax-1)=-py$。 $$ ax-1=-py$$ $$ax+py=1 $$ $a\perp p,\gcd(a,p)=1$,这个方程恰好就是扩展欧几里得的标准形式。 时间复杂度近似于 $O(\log p)$。参考代码为分数取模。 ```cpp ll exgcd(ll a,ll b,ll& x,ll& y) { if(!b) { x=1;y=0; return a; } int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } int main() { a=read();b=read(); ll x=0,y=0; exgcd(b,MOD,x,y); cout<<(1ll*(x%MOD+MOD)%MOD*a%MOD)<<endl; return 0; } ``` #### 3.3 欧拉定理 前置知识:**唯一质数分解定理**。 任何一个整数 $n>1$ 都可以唯一地分解为一组质数的乘积。 $$ n=2^{k_1}\times 3^{k_2}\times 5^{k_5}\times \cdots=\prod ^{\infty}_{i=1}p_i^{k_i} $$ **欧拉函数** $\varphi(n)$ 表示小于 $n$ 且与 $n$ 互质的正整数个数。 **性质:** 当 $a\perp b$,$\varphi(a)\varphi(b)=\varphi(ab)$。 **欧拉定理**:如果正整数 $n\perp a$,那么, $$ a^{\varphi(n)}\equiv 1(\bmod\ n) $$ 运用到这里,就有: $$ a\times inv(a)\equiv a^{\varphi(p)}(\bmod\ p)$$ $$\therefore inv(a)=a^{\varphi(p)-1}(\bmod\ p) $$ 我们可以在 $O(\sqrt n)$ 的时间内求出 $\varphi(p)$。 根据唯一分解定理,设 $n=\prod^m_{i=1} p_i^{k_i}$,则: $$ \varphi(n)=n\times \prod^m_{i=1} (1-\dfrac{1}{p_i}) $$ 证明: 首先考虑 $\varphi(p^k)$,其中 $p$ 是质数,$k$ 是非负整数。 如果要让 $\gcd(p^k,t)\not=1$,只能使 $t$ 为 $p$ 的倍数,即$t=p,2p,3p,\cdots,p^{k-1}\cdot p$,总共 $p^{k-1}$ 个。所以: $$ \varphi(p^k)=p^k-p^{k-1}=p^k(1-\dfrac{1}{p}) $$ 由于各质数幂之间互质,此时欧拉函数满足积性函数性质,得: $$ \varphi(n)=\varphi(\prod p_i^{k_i})$$ $$\varphi(n)=\prod^m_{i=1}\varphi(p_i^{k_i})$$ $$\varphi(n)=\prod^m_{i=1}p_i^{k_i}\times \prod^m_{i=1}(1-\dfrac{1}{p_i})$$ $$\varphi(n)=n\times \prod^m_{i=1}(1-\dfrac{1}{p_i}) $$ 接下来就可以根据这个等式求 $\varphi(p)$。我们可以在 $O(\sqrt n)$ 内枚举 $n$ 的质因数 $p_i$。令 $res=n$,枚举到每个 $p_i$, $res$ 就乘上 $(1-\dfrac{1}{p_i})$。当然也可以选择乘上 $\dfrac{p_i-1}{p_i}$。 为了方便判断是否全部 $p_i$ 都枚举到,我们可以不断除以 $p_i$,防止重复计算并除掉合数,并在最后特判一下。 最后再用快速幂即可。 时间复杂度 $O(\log\varphi(p)+\sqrt n)$。参考代码为分数取模。 ```cpp ll phi(ll x) { ll res=x,t=x; for(ll i=2;i*i<=t;i++) { if(x%i==0) { res=res/i*(i-1)%MOD; while(x%i==0) x/=i; } } if(x>1) res=res/x*(x-1)%MOD; return res; } ll qpow(ll base,ll k) { int res=1; while(k) { if(k&1) res=1ll*res*base%MOD; base=1ll*base*base%MOD; k>>=1; } return res; } int main() { a=read();b=read(); cout<<1ll*a*qpow(b,phi(MOD)-1)%MOD<<endl; return 0; } ``` #### 3.4 线性递推 一波奇妙的推式子。 现在求 $k$ 的逆元。 令 $a\times k+b=p$。 $$ \because b\times inv(b)\equiv 1(\bmod\ p)$$ $$\therefore (p-a\times k)\times inv(b)\equiv 1(\bmod\ p)$$ $$\therefore p\times inv(b)-a\times k\times inv(b)\equiv 1(\bmod\ p)$$ $$\therefore -a\times k\times inv(b)\equiv 1(\bmod\ p)$$ $$\because a\times k+b=p$$ $$\therefore a=p\ \mathrm{div}\ k,b=p\bmod k$$ $$\therefore -(p\ \mathrm{div}\ k)\times inv(p\bmod k)\equiv \dfrac{1}{k}(\bmod\ p)$$ $$\therefore -(p\ \mathrm{div}\ k)\times inv(p\bmod k)\equiv inv(k) (\bmod\ p) $$ $\mathrm{div}$ 是整除。 $p\bmod k<k$,所以我们可以递推。显然边界就是 $inv(1)=1$。$1$ 在模任意正整数下逆元都是 $1$。 但是 $ -(p\ \mathrm{div}\ k)$ 是负数,在模 $p$ 意义下,我们会加上 $p$ 。也就是说: $$ inv(k)\equiv(p-p\operatorname{div}k)\times inv(p\bmod k)(\bmod\ p) $$ 时间复杂度 $O(p)$。参考代码为求出 $[1,p-1]$ 范围内所有整数的逆元。 ```cpp #include <iostream> using namespace std; typedef long long ll; const int MAXN=3e6+5; ll n,p; ll inv[MAXN]; int main() { scanf("%lld%lld",&n,&p); inv[1]=1; for(int i=2;i<=n;i++) inv[i]=(p-p/i)*inv[p%i]%p; for(int i=1;i<=n;i++) printf("%lld\n",inv[i]); return 0; } ``` ### 4. 参考资料 1. [知乎-初等数论笔记 Part 1: 欧拉定理](https://zhuanlan.zhihu.com/p/35060143)。 2. [洛谷日报第 #30 期](https://www.luogu.com.cn/blog/zyxxs/post-xiao-yi-jiang-tan-qian-tan-sheng-fa-ni-yuan#)。 3. [洛谷日报第 #205 期](https://www.luogu.com.cn/blog/1239004072Angel/post-shuo-xue-sheng-fa-ni-yuan)。