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

是什么

单位元

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

例如在加法运算中,某一元素 \(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<bits/stdc++.h>
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<<ans<<"\n";
    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<bits/stdc++.h>
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<<x<<" "<<y;
    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<bits/stdc++.h>
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<<choose(a,b)<<"\n";
    return 0;
}
posted @ 2025-08-22 15:14  sad_lin  阅读(51)  评论(0)    收藏  举报