ACM数论5--数论倒数(逆元)

ACM数论5–数论倒数(逆元)

数论倒数又称逆元,在数论中倒数(逆元)是有特殊意义的,而且在数论中a的倒数不是1/a了,而是x,瞒足a*x = 1(mod p)而 x 并不一定是1/a.

(a + b) % p = (a%p + b%p) %p (对)
(a - b) % p = (a%p - b%p) %p (对)
(a * b) % p = (a%p * b%p) %p (对)
(a / b) % p = (a%p / b%p) %p (错)

而逆元就是对这样的第四种情况下用的;把a/b转化为a*inv(b)就可以使用乘法的了。(其中inv(b)表示 b 关于 p 的逆元)

当然对逆元这一概念,只有当a 与 p 互质时才存在a关于p的逆元

接下来正篇来了

1. 费马小定理

a^(p-1) == 1 (mod p)
连边除以 a 得到 a ^ (p-2) = 1/a (mod p) 即
a ^ (p-2) = inv(a) (mod p)
然后用快速幂求一下就行了 复杂度为O(log n)
上代码好了

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);
}

2.用扩展欧几里得写的

a * x + b * y = 1
若a 与 b 互质, 则有解

而 x 就是 a 关于b 的逆元, 相应的 y 是 b 关于 a 的逆元;

证明就是,直接两边除以a 或 b 就行了

#include
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));
    }
}

你可能感兴趣的:(数论)