数论倒数,又称逆元

 

先讲求余的概念:

(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     (错)

 

为什么除法是错的呢?

举个例子: (100/50) % 20 = 2   !=   (100%20)/(50%20) % 20 = 0

 

对一些题目,我们需要在中间进行求余运算,否则数据太大可能会爆!那么是不是这个算式中出现了除法,那么我们就无法运算了呢?

当然不是!!

这个时候就需要逆元了!

 

我们知道

如果

a*x = 1

那么x是a的倒数,x = 1/a

但是a如果不是1,那么x就是小数

那数论中,大部分情况都有求余,所以现在问题变了

a*x  = 1 (mod p)

那么x一定等于1/a吗

不一定

所以这时候,我们就把x看成a的倒数,只不过加了一个求余条件,所以x叫做    a关于p的逆元

 

比如2 * 3 % 5 = 1,那么3就是2关于5的逆元,或者说2和3关于5互为逆元

这里3的效果是不是跟1/2的效果一样,所以才叫数论倒数

 

a的逆元我们用inv(a)表示

a和p互质,a才有关于p的逆元

那么(a/b)%p = (a * inv(b)) % p = (a % p * inv(b) % p) % p

 

逆元已经介绍清楚了,那么如何去求逆元呢?

 

方法一:

利用费马小定理

a^(p-1) ≡1 (mod p)

推导出:

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

再运用下我们之前提过的快速幂!

注意一下有求inv(a) 的前提是a和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

如果a,b互质,有解

则这个解的x 就是 a关于b的逆元

     y 就是 b关于a的逆元

 

简单的推导一下吧:

先两边%b:  a*x%b = 1 % b

       a*x = 1 (mod b)

所以 x 是 a关于b的逆元 

 

void ex_gcd(LL a,LL b,LL &x,LL &y,LL &d)
{
    if(b==0)
    {
        x=1;
        y=0;
    }
    ex_gcd(b,a%b , y, x, d);
    y -= (a/b)*x;
}

LL inv(int t , int p)
{
    LL d, x ,y;
    ex_gcd(t, p, x, y, d);
    return d == 1 ? (x % p + p) % p : -1;
}

 

方法三:

利用递归的方式求逆元

当p是一个质数的时候有

inv(a) = (p - p / a) * inv(p % a) % p

一直递归到1为止,因为1的逆元就是1

LL inv(LL t, LL p) {//求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下 
    return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
}

 

 

参考:https://www.cnblogs.com/linyujun/p/5194184.html