数论逆元是指 a*x = 1 (mod m) x为最小的整数
第一种,费马小定理,要求m为素数
用快速幂求pow(a,m-2) (mod m)
int power(int a,int b){
int ans = 1;
a %= m;
while(b){
if(b&1)
ans = ans * a % m;
b >>= 1;
a = a * a % m;
}
return ans;
}
第二种,用拓展gcd,要求a与m互质
ax + my = 1 (gcd(a,m) = 1),求解的x即为a关于m的逆元
int e_gcd(int a,int b,int& x,int& y){
if (b == 0) {x = 1;y = 0;return a;}
else{
int d = e_gcd(b, a % b, y, x);
y -= x * (a / b);
return d;
}
}
第三种,m为奇质数(所以m=2不满足),O(n)求n个数的逆元
inv[i] = (m-m/i)*inv[m%i]%m
证明:
设 t=m/i,k=m%i
则 t*i+k=0 (mod m)
同除i t+k*inv[i] = 0 (mod m)
移项 inv[i]*k = m-t (mod m)
同除k inv[i] = (m-t)*inv[k] (mod m)
代入 inv[i] = (m-m/i)*inv[m%i]%m
递推代码
#include<cstdio>
const int N = 200000 + 5;
const int MOD = (int)1e9 + 7;
int inv[N];
int init(){
inv[1] = 1;
for(int i = 2; i < N; i ++){
inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
}
}
int main(){
init();
}
递归代码
#include<cstdio>
typedef long long LL;
LL inv(LL t, LL p) {//求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下
return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a%p, p));
}
}