【算法笔记】乘法逆元

  • 本文总计约 8000 字,阅读大约需要 30 分钟
  • 警告!警告!警告!本文有大量的 \(\LaTeX\) 公式渲染,可能会导致加载异常缓慢!

前言

逆元可能是数论里面最最最最最重要的知识点了,因为大部分的 OI 题目里,都需要选手将答案对某个大质数(一般是 \(10^9+7\) 或者是 \(998244353\))取模,而在取模的过程中,因为取模不满足除法分配率,所以在遇见除法的时候,不能直接相除

但这并不意味着我们对除法取模没有办法。而我们实现它的方法,就是使用乘法逆元来完成的。

题目引入

我们定义组合数 \(\dbinom{n}{m}\) 代表从 \(n\) 个互不相同的元素中,选择其中 \(m\) 个,有多少种选法。

计算组合数的公式为:\(\dbinom{n}{m}=\dfrac{n!}{m!\cdot (n-m)!}\),其中 \(n!\) 代表 \(n\) 的阶乘,它的定义为 \(n!=1\cdot 2\cdot\,\cdots\,\cdot n\)

现在给你两个正整数 \(n,m\),且 \(n\ge m\),求 \(\dbinom{n}{m}\)\(998244353\) 取模后的值。

暴力解法

对于求组合数问题,有两种基本的算法:

  1. 通过组合数公式 \(\dbinom{n}{m}=\dfrac{n!}{m!\cdot (n-m)!}\) 暴力硬算。int 不够开 long longlong long 不够写高精。复杂度为 \(\Theta(n)\)

    然鹅如果 \(n\le 10^7\),写高精似乎会超时 QwQ。这也是为什么我们要让答案对 \(998244353\) 取模,根据同余定理,有 \((a\bmod c)(b\bmod c)=(ab)\bmod c\)。所以,无论 \(n\) 再怎么大,取模后的结果也总是能被一个 int 变量存储下。大部分 OI 题里,答案对大数取模,目的就是让输出的答案不要过大毕竟写高精是很麻烦的一件事 QwQ

    但是我们会发现,计算组合数的过程中,需要使用除法。这里要强调一下:\(\left(\dfrac{a}{b}\right) \bmod c\neq \left(\dfrac{a\bmod c}{b\bmod c}\right)\bmod c\)!例如,\(\dfrac{10}{2} \bmod 3=5\bmod 3=2\),但 \(\dfrac{10\bmod 3}{2\bmod 3}=\dfrac{1}{2}\),两者显然不相等。所以,我们不能直接对除法运算取模

  2. 可以通过杨辉三角来计算组合数。根据公式 \(\dbinom{n}{m}=\dbinom{n-1}{m}+\dbinom{n-1}{m-1}\),我们可以存储先前已经算出来的组合数,然后递推地求出其它的组合数。

    时间复杂度是 \(\Theta(n^2)\),比暴力硬算的效率低,却可以绕过除法取模的问题。但当 \(n\le 10^7\) 的时候,好像会超时 QwQ,所以我们只能放弃掉这个算法了。

乘法逆元

除法取模

正如上面所说,杨辉三角的算法时间复杂度太低,所以我们还是得回到 \(\Theta(n)\) 的公式爆算中来。既然如此,我们就必须得直面除法取模的算法。

我们考虑最简单的情况,即 \(\dfrac{a}{b}\)\(c\) 取模:

\[\dfrac{a}{b}\equiv M\pmod{c}.\qquad(1.1) \]

我们在 \((1.1)\) 式两侧同时乘上一个 \(b\),那么上式变为:

\[a\equiv Mb\pmod{c}.\qquad(1.2) \]

现在,让我们再考虑这个同余方程:

\[bx\equiv 1\pmod{c}.\qquad(1.3) \]

假如我们能够解出方程 \((1.3)\) 中的 \(x\),再将其代入到 \((1.2)\) 中,就有了:

\[ax\equiv M\pmod{c}.\qquad(1.4) \]

同时联立 \((1.1)(1.4)\),我们就可以得出:

\[\dfrac{a}{b}\equiv ax\pmod{c},\text{ which }bx\equiv 1\pmod c\qquad(1.5) \]

也就是说,假如我们可以求出上面方程 \(3\) 中的 \(x\) 的值,我们就可以通过 \((1.5)\) 式求出 \(\dfrac{a}{b}\)\(c\) 取模的结果了。

乘法逆元的概念

假如对于一个正整数 \(a\),存在一个整数 \(x\),使得同余方程 \(ax\equiv 1\pmod p\) 成立,那么我们称 \(x\)\(a\) 在模 \(p\) 意义下的逆元,记做 \(x\equiv a^{-1}\pmod p\)

它的作用是什么……当然就是向上面一样,解决除法取模的问题。因为除法是乘法的逆运算,我们就可以认为,在模 \(p\) 的意义下,除以一个整数,和乘以这个整数在模 \(p\) 意义下的逆元,效果是完全相同的。

乘法逆元的性质

我们对上面的乘法逆元的概念有了基本的了解,并且还可以通过观察,简单地得到一些乘法逆元的性质:

  1. 在模 \(p\) 意义下,任意一个数的乘法逆元一定小于 \(p\) 本身。
    这个结论似乎很显然。因为我们不妨设 \(a\) 的乘法逆元为 \(x\),且 \(x>p\),那么一定有 \(a(x\bmod p)\equiv ax\equiv 1\pmod p\),所以 \((x\bmod p)\)\(a\) 的乘法逆元,且 \((x\bmod p)<p\),所以 \(a\) 的乘法逆元小于 \(p\)

  2. 在模 \(p\) 意义下整数 \(a\) 存在逆元,当且仅当 \(p\perp a\)
    不妨设 \(a^{-1}=x\),则:

    \[ax\equiv 1\pmod p.\qquad(2.1) \]

    稍微对 \((2.1)\) 做一下变形,就有:

    \[ax=kp+1,k\in\mathbb{Z}.\qquad(2.2) \]

    移项,得:

    \[ax+kp=1.\qquad(2.3) \]

    注意到这是一个关于 \(k,x\) 的二元一次不定方程。我们利用 Bézout 定理,可知,\(x\) 存在解,当且仅当 \(\gcd(a,p)=1\),即 \(a\perp p\),命题得证。

    同时,我们也可以得到另外一个结论:在模 \(p\) 的意义下,\(1,2,\cdots,p-1\) 都存在逆元,当且仅当 \(p\) 是一个质数。

  3. \(p\) 是质数,那么在模 \(p\) 意义下,\(1\) 的逆元为 \(1\)\((p-1)\) 的逆元为 \((p-1)\),除此以外,\(2\)\((p-2)\) 之间,任何一个数和它的逆元一一对应

    这个结论又有一个高大上的名字,叫做 Wilson 定理:

    当且仅当 \(p\) 是一个质数时,\((p-1)!\equiv -1\pmod p\)

    证明如下(数学证明警告!):

    先证必要性,即 \((p-1)!\equiv-1\pmod p\Rightarrow\) \(p\) 是一个质数:

    \(p\) 不是一个质数,存在正整数 \(a,b\) 使得 \(p=ab\),显然,\(a<p-1\)\(b<p-1\)

    \(a\neq b\) 时,\((p-1)!=1\cdot2\cdot\ \cdots\ \cdot a\cdot\ \cdots\ \cdot b\cdot\ \cdots\ \cdot(p-1)\),所以 \(ab\mid (p-1)!\)

    \(a=b\) 时,\((p-1)!=1\cdot2\cdot\ \cdots\ \cdot a\cdot\ \cdots\ \cdot 2b\cdot\ \cdots\ \cdot(p-1)\).

    综上,此时 \(p\mid(p-1)!\)。所以必要性成立;

    再证充分性,即 \(p\) 是一个质数 \(\Rightarrow(p-1)!\equiv-1\pmod p\)

    显然 \(p=2,3\) 时,命题成立;

    对于任意质数 \(p\ge 5\),令 \(A=\{2,3,\cdots,p-2\}\),对于任意 \(a\in A\),令 \(B=\{a,2a,\cdots,(p-1)a\}\)

    任取 \(1\le x<y\le p-1\),那么 \(xa\neq ya\),且 \(xa,ya\in B\),由于 \((y-x)a\in B\),所以 \(p\nmid(ya-xa)\),所以对于任意 \(xa,ya\in B\)\(xa\not\equiv ya\pmod p\)

    所以 \(B\) 中任意两个元素模 \(p\) 不同余。

    \(C=\{b|b=x\bmod p,x\in B\}\),则 \(C=\{1,2,3,\cdots,p-1\}\)。设 \(ax\equiv1\pmod{p}\),则:

    \(x\neq1\),假设 \(x=1\),则 \(ax\equiv a\equiv1\pmod{p}\),即 \(a=1\notin A\),与 \(a\in A\) 矛盾;

    同理,\(x\neq p-1\)

    \(x\neq a\),假设 \(x=a\),则 \(a^2\equiv1\pmod{p}\),可知 \((a-1)(a+1)\equiv0\pmod{p}\),解得 \(a=1\)\(a=p-1\),即 \(a\notin A\),舍去。

    所以 \(a\in A\) 时,存在唯一的 \(x\in A\),使得 \(ax\equiv1\pmod p\),且任意两个 \(a,x\) 相互对应。

    因此充分性成立。\(\Box\)

接下来,在了解了这 \(3\) 条性质之后,我们就要想想乘法逆元的求法了。

乘法逆元的求法

说了这么多,我们现在已经知道了模数,那么我们应该怎么求一个数 \(a\) 的逆元呢?

当然可以暴力解同余方程 \(ax\equiv1\pmod p\),时间复杂度为 \(\mathcal{O}(p)\)超时了别赖我 QwQ

我们能不能用一种更快的算法来求逆元呢?当然可以,有下面的这 \(3\) 种求法供你选择:

扩展 Euclid 算法

我们考虑 \((2.3)\) 的不定方程,其中 \(x\)\(a\) 的逆元。

一说到不定方程,我们马上就应该想到:用扩欧啊!

于是,这段代码就此产生了:

//exgcd 求逆元
int inv(int a, int p, int & x, int & k) {
	if(p == 0) {
		x = 1;
		k = 0;
	}
	
	else {
		inv(p, a % p, k, x);
		k -= a / p * x;
	}
	
	return (x % p + p) % p;  //注意求出的逆元可能是负数,我们要将其变为正的
}

它的时间复杂度为 \(\mathcal{O}(\log n)\)

Fermat 小定理

Fermat 小定理是一个关于同余的问题,它的表述如下:

\(p\) 是质数且 \(a\perp p\),则 \(a^{p-1}\equiv 1\pmod p\)

下面给出证明(数学证明警告!):

我们考虑一系列数 \(1,2,\cdots,p-1\),由于 \(p\) 是质数,所以它们均与 \(p\) 互质。

假如 \(a\perp p\),我们不妨设 \(a,2a,3a,\cdots,(p-1)a\),可以证明它们模 \(p\) 两两不同余(证明方法可以参照上面 Wilson 定理的证明),所以它们对 \(p\) 取模后,调整顺序,依旧为 \(1,2,\cdots,p-1\)

\(a\cdot 2a\cdot 3a\cdot\ \cdots\ \cdot(p-1)a\equiv 1\cdot2\cdot3\cdot\ \cdots\ \cdot(p-1)\pmod p\)

\(a^{p-1}\cdot(p-1)!\equiv(p-1)!\pmod p\),两边同时除以 \((p-1)!\),得 \(a^{p-1}\equiv 1\pmod p\)\(\Box\)

当然,不看证明也是可以的。如果是要求逆元的话,我们只需要将这个定理小小地变形,则有 \(a\cdot a^{p-2}\equiv 1\pmod p\)。于是有 \(a^{-1}\equiv a^{p-2}\pmod p\),我们可以通过快速幂来求出 \(a^{p-2}\)\(p\) 取模的结果了,时间复杂度依旧为 \(\Theta(\log n)\)

线性求逆元

\(\mathcal{O}(\log n)\) 的算法似乎非常高效了,但是还不够高效。回到题目引入中的问题,如果 \(m\le 10^7\) 的话,就意味着我们需要至少 \(m\) 次计算阶乘中每一个数的逆元,每次都是 \(\mathcal{O}(\log m)\),总复杂度为 \(\mathcal{O}(m\log m)\),在 \(m\le 10^7\) 的时候可能会被卡掉。

我们依旧需要尝试减少重复计算同一个逆元的次数,将其时间复杂度变为 \(\mathcal{O}(1)\)。如下:

有一个很显然的结论:

\[p\equiv 0\pmod p\qquad(3.1). \]

因为商乘除数加余数等于被除数,所以有

\[p=x\left\lfloor\dfrac{p}{x}\right\rfloor+(p\bmod x)\qquad(3.2). \]

代入 \((3.1)\) 式,移项,得:

\[x\left\lfloor\dfrac{p}{x}\right\rfloor\equiv-(p\bmod x)\pmod p\qquad(3.3). \]

两边同乘 \((p \bmod x)^{-1}\),得:

\[(p \bmod x)^{-1}\cdot\left\lfloor\dfrac{p}{x}\right\rfloor\cdot x\equiv -1\pmod p\qquad(3.4). \]

根据乘法逆元的定义,我们有

\[x^{-1}\equiv p-(p \bmod x)^{-1}\cdot\left\lfloor\dfrac{p}{x}\right\rfloor\pmod p\qquad(3.5). \]

\((3.5)\) 式可知,如果我们已经求出了 \(1\)\(p\bmod x\) 的逆元,那么我们就可以通过 \(\mathcal{O}(1)\) 递推转移过来 \(x\) 的逆元,这个过程中,求出 \(1\sim n\) 的逆元的时间复杂度是线性的 \(\Theta(n)\),而非 \(\mathcal{O}(n\log n)\),这样,就可以通过 \(n\le 10^7\) 大数据了。

代码如下:

int inv[p];
inv[1] = 1;  //初始化 1 的逆元为 1

for(int i = 2; i <= p; ++i) {
	inv[i] = (long long)(p - p / i) * (p % i) % p;  //转移逆元
}

当然,虽然第三种算法的效率更高,但是它不大能用来求单个数的逆元,而且还浪费了 \(\mathcal{O}(n)\) 的辅助数组的空间复杂度。所以,选择哪种算法,要看具体题目情景里面是如何要求的。

有理数取模

那么,有了乘法逆元之后,我们就可以将取模的运算从整数推广到有理数

假如一个有理数 \(q=\dfrac{a}{b}\),我们要求 \(q\bmod p\) 的值,其中 \(p\perp b\)。根据 \((1.5)\) 式,我们不难得出 \(q\equiv ab^{-1}\pmod p\)

也就是说,我们可以将取模运算推广到有理数,这样,就可以将其用于求概率或者数学期望时候的取模了。

例题

本题目列表会持续更新。

参考文献

posted @ 2022-02-01 21:40  CaO氧化钙  阅读(313)  评论(1)    收藏  举报