数论之旅(一): 逆元逆元!

1.基本概念:逆元可以理解为乘法中的倒数!

2.基础知识准备: 求余数。

前提条件: a,p互质。
在这里我们需要了解 (a / b) % p = (a%p / b%p) %p 这样是错的。
举个简单的反例 (10 / 4)% 3 = 2, (10 % 3 / 4 % 3) % 3 = 0. 很明显不一样。

而对于加法,减法,乘法而言都是正确的。

所以,我们需要引入 逆元 的概念,来帮助我们解决问题—>对于一些题目,我们必须在中间过程中进行求余,否则数字太大,会爆范围。 我们假设逆元为 inv(b)。
(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p。

实现 除法—->乘法的转变。

接下来我们来探究如何求解逆元。

方法一: 费马小定理。a^(p-1) ≡1 (mod p) (p为质数)

->>a^(p-2) ≡inv(a) (mod p) ->> inv(a)=a^(p-2)

这里可以直接用快速幂算出。复杂度 longn。

方法二:扩展欧几里德算法

欧几里德有个十分有用的定理: gcd(a, b) = gcd(b , a%b) 。
所以我们可以在log的时间里求出 a,b的gcd。

a*x + b*y = 1 (a,b互质)
对于两边%b
a*x % b + b*y % b = 1 % b

a*x % b = 1 % b

a*x = 1 (mod b)
-> x是a关于b的逆元

 ll gcd(ll a,ll b){
     return b == 0 ? a : gcd(b, 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));
    }
}

推荐习题: hiho1530 分数取模。

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