方法一:扩展欧几里得
a∗b≡1(modp) ;
a∗b+k∗p=1 ;
LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得算法
{
if(b==0)
{
x=1,y=0;
return a;
}
LL ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
LL getInv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1
{
LL x,y;
LL d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}
性能分析:
- 时间复杂度:O(logn)(实际是斐波那契数列)
- 适用范围:只要存在逆元即可求,适用于个数不多但是mod很大的时候,也是最常见的一种求逆元的方法。
方法二:费马小定理/欧拉定理
费马小定理:若p为素数,则有ap−1≡1(modp) ;
a^p−2∗a≡1(modp) ;
a^p−2 就是a在mod p意义下的逆元。
欧拉定理:若a、p互素,则有a^φ(p)≡1(modp) (费马小定理的一般形式)
a^φ(p)-1∗a≡1(modp)
a^φ(p)−1 就是a在mod p意义下的逆元。
LL qkpow(LL a,LL p,LL mod)
{
LL ans=1,k=a%mod;
while(p)
{
if(p&1)ans=ans*k%mod;
k=k*k%mod;
p>>=1;
}
return t;
}
LL getInv(LL a,LL mod)
{
return qkpow(a,mod-2,mod);
}
性能分析:
- 时间复杂度:O(logmod)
- 适用范围:一般在mod是个素数的时候用,比扩欧快一点而且好写。
- 但是如果是合数,相信一般没人无聊到去算个欧拉函数。