数论倒数(逆元)

参考自: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();
}

猜你喜欢

转载自blog.csdn.net/qq_38367681/article/details/81432583