浅谈逆元及其求法

内容部分参考于以下博客

https://www.cnblogs.com/rir1715/p/7748054.html#commentform

https://baijiahao.baidu.com/s?id=1609032096408414934&wfr=spider&for=pc

用途:

就博主现在的认知来说(毕竟是一个蒟蒻,如果有新的认识会更新的),逆元主要是用在除法取模的时候。首先,如果是乘法,那么有 a*b%mod  = (a%mod*b%mod),这是显而易见的一个结论,但当我们遇到除法取模时(一般是在组合数取模的时候),就不会这么容易了,因为(a/b%mod !=((a%mod/b%mod)),这个时候就需要我们的逆元登场了,至于为什么,请看下面逆元的定义。

定义:

简单来说,逆元其实就相当于初中学的倒数。

我们知道,对于一个数a,我们可以定义一个数b,使得 a*b = 1,这样的话,当我们需要除以a时,就可以转化为乘b,这样就能把除法转化为乘法。

同样,对于除a取模于mod的一个运算,我们依然可以定义一个数b,使得

num/a)%mod = (num*b)mod,这样的一个数b,就可以称b为a在模mod下的一个逆元,有了逆元的话,我们就能将除法的取模运算转化为乘法的取模运算,从而来解决一些问题。

在这里给出逆元定义的数学形式(假设模数是n)

a * b ≡ 1 (mod n)

最后,要注意一点,一个数a在模n的情况下是不一定有逆元的,a关于n的逆元存在当且仅当a 和 n 互质,一个数a在模n的情况下逆元也不一定唯一,但我们只需要求出一个就够的,因为他们在模 n 的条件下结果是相同的。

求法:

目前,求解逆元一共有三种方法,接下来将一一讲解。

整数为 a,模数为 p,a在mod p条件下的逆元为 x。

1. 费马小定理

应用范围:p为素数。

这应该是最常用的的求逆元的方法,虽然有所局限,但确实是使用频率最高的一种算法了。

原理:

首先,我们由费马小定理得

   a^(p - 1) ≡ 1 mod p

变形得

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

对比于逆元的定义

  a * x ≡ 1 mod p

显然 我们可以得到

  x  ≡ a^(p-2) mod p

这样,我们就由费马小定理推理出 a 在 mod p 条件下的逆元为 a^(p-2)。

代码实现:

typedef long long LL
LL quick_pow(LL x, LL n LL p)// 快速幂求x^n mod p 的结果 { if(n==0) return 1; if(n==1) return x%p; LL ans = 1; LL tmp = x%p; while(n) { if(n&1) { ans = (ans*tmp)%p; } tmp = tmp*tmp%p; b>>=1; } return ans%p; } LL inv(LL b,LL p) //求数 b mod p 的逆元 { return quick_pow(b,p-2,p); }

 

2. 拓展欧几里得

局限:难以理解?(手动狗头)

原理:

敲起来太麻烦了,手写了

 

 

代码实现:

typedef long long LL
LL ex_gcd(int a,int b,int &x,int &y)//注意x,y的传引用
{
    if(a==0&& b==0)
        return -1;
    if(b==0)//递归终止条件
    {
        x = 1;
        y = 0;
        return a;
    }
    int ans  = ex_gcd(b,a%b,x,y);
    int t = x; // 扩欧方程的解
        x = y;
        y = x - a/b*y;
    return ans;
}
LL inv(LL b,LL p) //求数 b mod p 的逆元
{
    int x,y;
    int d = ex_gcd(b,p,x,y);
    if(d==-1) // b,p不互素,利用扩欧求不出逆元
        return - 1else
     return (x+p)%p; //x 的值就是 逆元,加上p再取模消除负数的情况
}

 

3. 线性推

原理:

 

 

因此我们可以通过一个递推式在线性时间内推导出1——(p-1)mod p 的逆元,递推式如下注意在式子右边加上p来消除负号

  inv[i]=(p-p/i)*inv[p%i]%p;

代码实现:

typedef long long LL
LL inv_arr[maxn];
void inv(LL b,LL p) //可以得到1到b mod p 的逆元 (1 < b < p)
{
    inv_arr[1] = 1;
    for(int i = 2;i<=b;i++)
        inv_arr[i] = (p-p/i)*inv_arr[p%i]%p;
    return;
}

如果还有其他疑问,欢迎在评论区留言

posted @ 2019-07-28 00:24  浅花迷人  阅读(...)  评论(...编辑  收藏