逆元
关于逆元的4种求法总结。
扩欧求解
运用扩展欧几里得求解逆元。
\(ax+by=1\) 其中 \(a\) 是将求的数,\(x\) 是逆元,\(b\) 是模数。
int exgcd(int a,int b,int &x,int &y)
{
if(!b){x=1,y=0;return a;}
int res=exgcd(b,a%b,x,y);
int tmp=x;
x=y;y=tmp-a/b*y;
return res;
}
int exgcd_inv(int a,int mod)
{
int d,x,y;
d=exgcd(a,mod,x,y);
return d==1?(x+mod)%mod:-1;
}
费马小定理求解
利用快速幂和费马小定理。
当 \(p\) 为质数时,有 \(a^{p-1}\equiv 1\ (mod\ p)\) 。
可得: \(a·a^{p-2}\equiv 1\ (mod\ p)\) ,\(a^{p-2}\) 就是 \(a\) 的逆元。
int ksm(int x,int y,int mod)
{
int res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res;
}
int fermat_inv(int x,int mod){return ksm(x,mod-2,mod);}
线性求逆元
证明:
首先,\(1^{-1}\equiv 1\ (mod\ p)\)
设 \(p=k*i+r,r<i\) ,可得 \(k\times i+r\equiv 0\ (mod\ p)\) 。
两边同乘 \(i^{-1},r^{-1}\) 得 \(k\times r^{-1}+i^{-1}\equiv 0\ (mod\ p)\)
移项得 \(i^{-1}\equiv -k\times r^{-1}\) ,消元得 \(i^{-1}\equiv -\lfloor \frac{p}{i}\rfloor\times (p\ mod\ i)\ (mod\ p)\)
因为此时求的逆元为负数,我们需要将它变正,可以加上一个 \(p\times r^{-1}\) 。
最后得 \(i^{-1}\equiv (p-\lfloor \frac{p}{i}\rfloor)\times (p\ mod\ i)\ (mod\ p)\)
inv[1]=1;
for(int i=1;i<mod;++i)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
递归求逆元
我们可以从线性求逆元发现, \(i\) 的逆元只与 \(p\ mod\ i\) 有关,所以我们可以递归求解逆元,时间复杂度和费马小定理求解都是 \(O(logn)\) 的时间复杂度。
为了防止爆 $long\ long $ ,我们可以用 __int128 进行乘法取模来保证正确性。
int gsc(int x,int y){return (__int128)x*y%p;}
int inv(int x){return x==1?1:gsc(p-p/x,inv(p%x));}
这种求逆元方法代码极短!很方便!
总结
扩展欧几里得求解是最快的,常数小,但比较难写。
费马小定理求解从各各方面来看都没有递归求解好,所以很不理解很多人推崇它的理由。
线性求逆元是 \(O(n)\) 时间复杂度,在大量用到逆元时适合用线性来预处理逆元。