【数论】小刻都能看懂的乘法逆元

【数论】小刻都能看懂的乘法逆元

是什么

单位元

单位元是集合里的一种特别的元,与该集合里的运算(可理解为实数里的*,但并不局限于)有关。当它和其他元素结合时,并不会改变那些元素。

例如在加法运算中,某一元素 \(a\) 和另一元素 \(e\),满足 \(a+e=a\),那么 \(e\) 就是加法运算中的单位元,显然加法中 \(e=0\),乘法中 \(e=1\)。

模乘下的单位元

在模 \(n\) 的乘法下,所有模 \(n\) 和 \(a\) 同余的数都可以表示为:

\[a \pmod{n}=kn+a,(k\in \mathbb{Z})

\]

令单位元为 \(e\),然后将 \(a\) 与 \(e\) 进行模乘运算,得到:

\[\begin{align*}

a \pmod{n} \times e\pmod{n} &=(k_1n+a)(k_2n+e)\\

&=(k_1k_2n+k_1e+k_2a)n+ae

\end{align*}

\]

根据单位元的定义可知模乘运算下单位元 \(e=1\)。

逆元

逆元,是指一个可以取消另一给定元素运算的元素,也是在此运算下相结合后得到单位元的两个元素。

例如在加法运算中,某一元素 \(a\) 和另一元素 \(e\),满足 \(a+e=0\),那么 \(a\) 与 \(e\) 就互为逆元,显然加法中 \(e=-a\),乘法中 \(e=\frac{1}{a}\)。

在模乘运算中,任意整数 \(a\pmod{n}\) 的逆元表示为: \(a^{-1}\pmod{n}\)

根据逆元的定义满足下列等式:\(aa^{-1} \equiv 1 \pmod{n}\)

裴蜀定理

设 \(a,b\) 是不为零的整数,那么对于任意整数 \(x,y\),都有 \(\gcd(a,b)\mid ax+by\) 成立;而且,存在整数 \(x,y\),使得 \(ax+by=\gcd(a,b)\) 成立。

逆元存在的必要条件

必须满足 \(\gcd(a,n)=1\),即 \(a\) 与 \(n\) 互素。

根据裴蜀定理得:

\[ax+nk=\gcd(a,n)=1

\]

将此式对 \(n\) 取模,得到:

\[ax\equiv1 \pmod{n}

\]

这就意味着 \(x\) 为 \(a\) 在模 \(n\) 下的乘法逆元。

怎么求

费马小定理

给定素数 \(p\) 和正整数 \(a\),一定满足 \(a^{p-1}\equiv 1\pmod{p}\)。

再经变形可得 \(aa^{p-2}\equiv 1\pmod{p}\)。

由此可知 \(a^{p-2}\pmod{p}\) 为 \(a\) 的逆元。

关于 \(a^{p-2}\) 用快速幂求得即可。

对应代码:

#include

using namespace std;

int a,b;

int ans=1;

void quick(int a,int k){

int base=a;

while(k){

if(k&1){

ans=ans*base;

ans%=b;

}

base=base*base%b;

k>>=1;

}

}

signed main(){

cin>>a>>b;

quick(a,b-2);

cout<

return 0;

}

拓展欧几里得算法 exgcd

给定正整数 \(a\) 和 \(b\),求满足等式 \(ax+by=1\) 的 \(x\) 的最小正整数。如果不存在返回 \(-1\)。

先提取出 \(a\) 和 \(b\) 的最大公约数,即 \(g=\gcd(a,b)\),则等式可以转化为:

\[g(\frac{a}{g}x+\frac{b}{g}y)=1

\]

两数相乘结果为 \(1\),要么都是 \(1\),要么都是 \(-1\),而最大公约数不可能为负数,所以 \(\gcd(a,b)=1\),那么有解的条件就是 \(a\) 和 \(b\) 一定互素。

根据裴蜀定理可以把原式转化为:\(ax\equiv1 \pmod{b}\),可以用费马小定理解得。

言归正传,回到 exgcd 上,我们对原始公式进行变形可得:

\[\begin{align*}

ax+by &=1\\

&=gcd(a,b)\\

&=gcd(b,a\bmod b)\\

&=bx^{\prime}+(a\bmod b)y^{\prime}\\

&=bx^{\prime}+(a-b\lfloor \frac{a}{b} \rfloor)y^{\prime}\\

&=ay^{\prime}+b(x^{\prime}-\lfloor \frac{a}{b} \rfloor y^{\prime})

\end{align*}

\]

这是个递推式,我们可以递归求解,什么时候递归结束呢。

欧几里得算法计算两个互质的数到 \(gcd(1,0)\) 将返回,此时式子为

\[gcd(1,0)=ax+by=1=1\times x+0\times y

\]

此时 \(x\) 为 \(1\),\(y\) 为任意值。

对应代码:

#include

using namespace std;

int a,b;

int x,y;

void exgcd(int a,int b){

if(b==0){

x=1;

y=0;

return;

}

exgcd(b,a%b);

int t=x;

x=y;

y=t-a/b*y;

return;

}

signed main(){

cin>>a>>b;

exgcd(a,b);

cout<

return 0;

}

逆元的应用

组合数

根据组合数计算公式:\(C_n^{m}=\frac{n!}{m!(n-m)!}\)

要计算阶乘然后除阶乘,但是除法不满足模运算,而逆元相当于倒数,用逆元阶乘来实现除法。

阶乘:\(fac[i]=fac[i-1]*i\)

阶乘逆元:\(\frac{1}{(n-1)!}=inv[i-1]=inv[i]*i=\frac{1}{n!}\times n\)

原理:\(fac[n]*inv[n]=1=n!\times \frac{1}{n!}\)

所以只需要求出 \(fac[n]\) 的逆元 \(inv[n]\) 即可。

#include

using namespace std;

const int N=2e5+10;

const int mod=998244353;

int a,b;

int fac[N],inv[N];

int choose(int n,int m){

return fac[n]*inv[m]%mod*inv[n-m]%mod;

}

int quick(int a,int k){

int base=a%mod;

int ans=1;

while(k){

if(k&1){

ans=(ans*base)%mod;

}

base=(base*base)%mod;

k>>=1;

}

return ans;

}

signed main(){

fac[0]=1;

for(int i=1;i<=100005;i++){

fac[i]=fac[i-1]*i%mod;

}

inv[100005]=quick(fac[100005],mod-2);

for(int i=100004;i>=0;i--){

inv[i]=inv[i+1]*(i+1)%mod;

}

cin>>a>>b;

cout<

return 0;

}

相关创作

手机imei是什么意思啊
365彩票官方下载手机

手机imei是什么意思啊

08-27 👁 7798
豪华专车平台
365彩票官方下载手机

豪华专车平台

09-23 👁 545