【数论】小刻都能看懂的乘法逆元
是什么
单位元
单位元是集合里的一种特别的元,与该集合里的运算(可理解为实数里的*,但并不局限于)有关。当它和其他元素结合时,并不会改变那些元素。
例如在加法运算中,某一元素 \(a\) 和另一元素 \(e\),满足 \(a+e=a\),那么 \(e\) 就是加法运算中的单位元,显然加法中 \(e=0\),乘法中 \(e=1\)。
模乘下的单位元
在模 \(n\) 的乘法下,所有模 \(n\) 和 \(a\) 同余的数都可以表示为:
令单位元为 \(e\),然后将 \(a\) 与 \(e\) 进行模乘运算,得到:
根据单位元的定义可知模乘运算下单位元 \(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\) 互素。
根据裴蜀定理得:
将此式对 \(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)\),则等式可以转化为:
两数相乘结果为 \(1\),要么都是 \(1\),要么都是 \(-1\),而最大公约数不可能为负数,所以 \(\gcd(a,b)=1\),那么有解的条件就是 \(a\) 和 \(b\) 一定互素。
根据裴蜀定理可以把原式转化为:\(ax\equiv1 \pmod{b}\),可以用费马小定理解得。
言归正传,回到 exgcd 上,我们对原始公式进行变形可得:
这是个递推式,我们可以递归求解,什么时候递归结束呢。
欧几里得算法计算两个互质的数到 \(gcd(1,0)\) 将返回,此时式子为
此时 \(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;
}