求逆元的几种方法

拓展欧几里得求逆元

原理

\(a*b\equiv 1 \pmod p\)

\(a*b+k*p = 1\)

代码

//拓展欧几里德求逆元
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1;y=0;
return a;
}
int ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
int getinv(int a,int m=mod){
int x,y;
int res=exgcd(a,m,x,y);
return res==1? (x%m+m)%m:-1;
}


性能分析

时间复杂度:O(logn)(实际是斐波那契数列)

适用范围:只要存在逆元即可求,适用于个数不多但是mod很大的时候,也是最常见的一种求逆元的方法。

费马小定理

原理

当p为素数时

\(a^{p-1}\equiv1\pmod p\)

\(a^{p-2}*a\equiv 1 \pmod p\)

代码

//费马小定理/快速幂求逆元
int ksm(int a,int b=mod-2,int m=mod){
int res=1;
while(b){
if(b&1){
res*=a;
res%=m;
}
a*=a;
a%=m;
b>>=1;
}
return res;
}


性能分析

O(logmod)

适用范围:mod是个素数的时候。

欧拉函数求逆元

原理

当a和p互素时

\(a^{\varphi(p)}\equiv 1 \pmod p\)

\(a^{\varphi(p)}*a\equiv 1 \pmod p\)

代码

//欧拉函数求逆元
int phi(int x)
{
int res=x,a=x;
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
{
res=res/i*(i-1);
while(a%i==0)
a=a/i;
}
}
if(a>1)
res=res/a*(a-1);
return res;
}

int ksm(int a,int b=phi(mod)-1,int m=mod){
int res=1;
while(b){
if(b&1){
res*=a;
res%=m;
}
a*=a;
a%=m;
b>>=1;
}
return res;
}


性能分析

O(logmod)

适用范围:mod是个合数的时候。

递推求逆元

原理

\(inv[i]=(mod-mod/i)*inv[mod\)%\(i]\)%\(mod\)

inv[1]=1;

代码

int inv[mod+5];
void init(int mod){
inv[1]=1;
for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}


性能分析

时间复杂度O(n)

适用范围:mod数是不大的素数