(ACM数论)求乘法逆元的各种姿势~

首先,通过下面的式子来看看什么是乘法逆元~

x * n % P = 1,其中x和P为已知且互素,n未知(比如在 2 * n % 7 = 1 这个式子里,n就是乘法逆元)

弄懂什么是乘法逆元,来看看有什么姿势可以把它求出来吧~

姿势1.暴力(此姿势不要求P为素数)

没有什么问题是一个暴力解决不了的,如果有,那就两个(手动滑稽)

代码如下:

int n;
for(int i = 1 ;i <= P ;i++)
{
    if(x * i % P == 1)
    {
        n = i;
        break;
    }
}

虽然暴力简单粗暴,但是它的时间复杂度为O(P),只能在P较小的情况使用,局限性有点大。

姿势2.快速幂(此姿势要求P为素数)

这个姿势是由费马巨巨的费马小定理(x^(P-1) ≡ 1 mod P)得来的:

x^(P-1) ≡ 1 mod P 即 x^(P-1) % P = 1

已知 x * n % P = 1

所以有 x * n % P = x^(P-1) % P 即 n % P = x^(P-2) % P

到这里可以看出,逆元有无数个:n,n+P,n+2*P,…

一般情况下我们只要求那个最小的正整数,这样问题就转化为求 x^(P-2) % P

代码就不贴了,在网上找个有求模快速幂的模版,把x,P-2,P代进去就可以了

这个姿势的时间复杂度为O(log P),是个不错的姿势(手动滑稽)

姿势3.扩展欧几里德算法(此姿势不要求P为素数)

首先,我们将式子变形一下~

x * n % P = 1 等价于 n * x + k * P = 1,其中k为整数

因为 x 和 P 互素,所以 gcd (x ,P) = 1(最大公约数为1)

所以有 n * x + k * P = gcd (x ,P)

这样问题就转化为,求二元一次方程 n * x + k * P = gcd (x ,P) 的解

代码同样不贴了, 在网上找个扩展欧几里德的模版,把 x , P 代进去。

不过要注意,算出来的n有可能是负数,也有可能不是最小的。若需要得到最小的正整数,加上如下代码即可:

n = n % P;//处理不是最小的情况
while(n < 0)//处理负数的情况
{
    n =( n + P ) % P;
}

到这里可以得出结论:逆元的集合为{…,n - 2 * P,n - P,n , n + P,n + 2 * P,… }

这个姿势的时间复杂度为O(log P),是个不错的姿势(手动滑稽)

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