求乘法逆元的5种方法
求正整数a关于1模p的的乘法逆元,即求满足ax≡1(mod p)的正整数解x。
一、
扩展欧几里得算法(O(logP))
将ax≡1(mod p)转化为解方程ax+py=1的正整数解x。
前提条件:a与p互质,此时解唯一。
代码:
int exgcd(int a,int b,int &x,int &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()
{
int p,x,y;
p=mod;
for(int a=1;a<=20;a++)
{
exgcd(a,p,x,y);
cout<<(x+p)%p<<endl;
}
}
二、费马小定理(O(logP))
前提条件:p为质数
则有ap−1≡1(mod p)
即a⋅ap−2≡1(mod p),
ap−2即逆元。通过快速幂可解。
三、欧拉定理求逆元(O(P
))
欧拉定理:若p,a为正整数,且p,a互质,则:
aϕ(p)≡1(mod p)
扫描二维码关注公众号,回复:
11517276 查看本文章
当p为质数时,即费马小定理。
因此,我们可以对p先求出欧拉函数ϕ(p),再用快速幂求出乘法逆元为aϕ(p)−1。
可用定义求欧拉函数,时间复杂度O(p
)
代码:
ll get_euler(ll C)
{
ll res=C;
for(int i=2;i<=C/i;i++)
if(C%i==0)
{
while(C%i==0) C/=i;
res=res/i*(i-1);
}
if(C>1) res=res/C*(C-1);
return res;
}
亦可用线性筛筛出[1,n]内的欧拉函数。
四、递推
求区间[1,n]的逆元,时间复杂度O(n)。
代码:
int inv[N];
inv[1]=1;
for(int i=2;i<=20;i++)
inv[i]=(long long)(mod-mod/i)*inv[mod%i]%mod;
五、递推求阶乘逆元
先求出n!的逆元,再倒序递推。
代码:
void init()
{
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%mod;
inv[n]=quick_pow(fac[n],mod-2,mod);
for(int i=n-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod;
}