参考自:http://www.cnblogs.com/linyujun/p/5194184.html
为什么要求逆元呢?主要是为了对两个相除的数的取余,我们都知道:
(a / b) % p ≠ (a%p / b%p) %p;
举个反例:(100/50)%20 = 2 ≠ (100%20) / (50%20) %20 = 0
这个时候我们能直接(a %p * 1 / b % p)%p吗?想法很棒但是答案不对。我们需要的不是1/b,而是inv(b),b的逆元,也就是数论倒数。b*x%p == 1;x就是b关于p的逆元。(x和p互质)
所以有哪些方法可以求逆元呢?
第一种方法:(当p为质数的时候)
费马小定理
a^(p-1) ≡1 (mod p)
两边同除以a
扫描二维码关注公众号,回复:
3435769 查看本文章
a^(p-2) ≡1/a (mod p)
应该写a^(p-2) ≡ inv(a) (mod p)
所以inv(a) = a^(p-2) (mod p)
LL pow_mod(LL a, LL b, LL p){//a的b次方求余p
LL ret = 1;
while(b){
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
LL Fermat(LL a, LL p){//费马求a关于b的逆元
return pow_mod(a, p-2, p);
}
第二种方法:
扩展欧几里得算法
a*x + b*y = 1
如果ab互质,有解
这个解的x就是a关于b的逆元
y就是b关于a的逆元
为什么呢?
你看,两边同时求余b
a*x % b + b*y % b = 1 % b
a*x % b = 1 % b
a*x = 1 (mod b)
所以x是a关于b的逆元
反之可证明y
#include<cstdio>
typedef long long LL;
void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
if (!b) {d = a, x = 1, y = 0;}
else{
ex_gcd(b, a % b, y, x, d);
y -= x * (a / b);
}
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d == 1 ? (x % p + p) % p : -1;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}
第三种方法:
#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();
}