前言

对于部分基础的数论知识点(如整除、同余等),笔者不会再进行讲解。因此,笔者希望有扎实的数论基础的读者来阅读这篇文章。


中国剩余定理

对于线性同余方程组 xai(modpi)x \equiv a_i \pmod{p_i},中国剩余定理能够求解其最小非负整数解。

这个算法要分为两种情况讨论,而两种情况的算法实际上存在本质区别。


模数两两互质

我们有如下基于构造思想的算法:

M=i=1mpi\displaystyle \text{M} = \prod_{i = 1}^mp_iMi=Mpi\text{M}_i = \dfrac{\text{M}}{p_i}Mi1\text{M}_i^{-1}Mi\text{M}_i 在模 pip_i 意义下的逆元,那么整个方程组的最小非负整数解为 (i=1maiMiMi1)modM\displaystyle (\sum_{i = 1}^m a_i\cdot \text{M}_i\cdot \text{M}_i^{-1}) \bmod \text{M}

证明:

考虑这个和式的第 iiaiMiMi1a_i \cdot \text{M}_i \cdot \text{M}_i^{-1},将其对 pip_i 取模即得到第 ii 个同余方程的特解 aia_i,而将其对其它的 pj (ji)p_j~(j \ne i) 取模均得到 00,所以这个项既能够满足第 ii 个同余方程,又不会对其它的同余方程造成影响。

这样,我们证明了这个和式是原方程的一个特解。

又因为方程组在 [0,M)[0, \text{M}) 内仅存在一个解,因此将这个和式对 M\text{M} 取模即得到最小非负整数解。

值得一提的是,我们熟知的拉格朗日差值实际上是基于这一部分的算法的。


模数不两两互质

我们是否能够考虑套用上述算法的框架呢?

不行,因为 Mi1\text{M}_i^{-1} 甚至有可能根本不存在。因此,仅对上述算法进行微调是不可行的。

我们考虑一个很简单的思想:合并同余方程。

对于前 ii 个同余方程,我们可以通过将它们合并得到一个形如 xbi(modMi)x \equiv b_i \pmod{\text{M}_i} 的方程,这里的 Mi\text{M}_i 表示前 ii 个模数的 lcm\operatorname{lcm}

接下来我们考虑其和第 i+1i + 1 个方程的合并。

首先,我们将满足前 ii 个方程的 xx 写成 bi+Mik (kZ)b_i + \text{M}_i \cdot k~(k \in \Z) 的形式,那么我们仅需求出 kk 的一个特解便可得到前 i+1i + 1 个方程的特解。然后,我们将其带入第 i+1i + 1 个方程得到 bi+Mikai(modpi)b_i +\text{M}_i \cdot k \equiv a_i \pmod{p_i},再将其转化为二元一次不定方程的形式,最后用 exgcd\operatorname{exgcd} 求出 kk 的一个特解即可。在这个过程中,我们还可以判断方程组是否存在无解的情况。


实际上,若我们对于每一个方程,都将 xx 带上一个 cic_i 的系数,那么无论是上述的哪一种情况,对应的算法经过调整仍然是可行的。

读者不妨自行思考,在 xx 带上了系数的情况下,两种情况对应的算法分别应该如何调整。


Lucas 定理

我们经常需要计算 (nm)modp\displaystyle \binom{n}{m} \bmod p 的值,而若 nnmm 较大,传统的算法是无法支持的。

是否有其它的算法呢?


模数为质数

Lucas\mathcal{Lucas} 定理是这样一个公式:

(nm)(npmp)(nmodpmmodp)(modp)\binom{n}{m} \equiv \binom{\displaystyle \lfloor \frac{n}{p} \rfloor}{\displaystyle \lfloor \frac{m}{p} \rfloor} \cdot \binom{n\bmod p}{m\bmod p} \pmod{p}

根据这个公式,我们可以预处理 n,m[0,p)n, m \in [0, p) 的答案,然后便可做到单次时间复杂度 O(logpn)\mathcal{O}(\log_pn) 地计算组合数。若 pp 的范围较小,那么这是一个复杂度十分优秀的算法。


Lucas\mathcal{Lucas} 定理还有一个十分优美的推论:

(nm)mod2\displaystyle \binom{n}{m} \bmod 2 的值为 11,当且仅当 n and m=mn~\texttt{and}~m = m,也即在二进制下 mmnn 的子集,其中 and\texttt{and} 表示二进制下的按位与运算。

因为当 p=2p = 2 时,Lucas\mathcal{Lucas} 定理会将 (nm)\displaystyle \binom{n}{m} 拆分为若干个 (00),(01),(10),(11)\displaystyle \binom{0}{0},\binom{0}{1},\binom{1}{0},\binom{1}{1},而其中只有 (01)\displaystyle \binom{0}{1} 等于 00,这正好对应了 n and mmn~\texttt{and}~m \neq m 的情况。

这个推论揭示了组合数的奇偶性与 nnmm 的二进制之间的关系。对于和组合数的奇偶性相关的问题,将其转化为和 nnmm 的二进制相关的问题,不失为一种好的办法。


模数不为质数

pp 不为质数时,上述定理是不成立的,因为其证明用到了和质数相关的性质。

对于模数为合数的求值问题,将其模数进行质因数分解,最后再用中国剩余定理合并答案,不失为一种好的办法。

我们按照上述方法操作,将问题转化为如何快速求 (nm)modpq\displaystyle \binom{n}{m} \bmod p^q

(nm)\displaystyle \binom{n}{m} 拆开得到 n!m!(nm)!\displaystyle \frac{n!}{m! \cdot (n - m)!},然后把三个部分包含的 pp 全部提取出来,原式将会转化为 n!pr1m!pr2(nm)!pr3pr1r2r3modpq\displaystyle \frac{\displaystyle \frac{n!}{p^{r_1}}}{\displaystyle \frac{m!}{p^{r_2}} \cdot \frac{(n - m)!}{p^{r_3}}} \cdot p^{r_1 - r_2 - r_3}\bmod p^q,此时分式的分母与模数互质,那么问题的关键就在于如何快速求 n!modpqn! \bmod p^q

OIWiki\mathcal{OI-Wiki},这里用 22!mod3222! \bmod 3^2 来举例。

先把 22!22! 拆开:

22!=1×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×19×20×21×2222! = 1\times2\times3\times4\times5\times6\times7\times8\times9\times10\times11\times12\times13\times14\times15\times16\times17\times18\times19\times20\times21\times22

考虑把 33 的倍数提出来,化简一下就有:

22!=37×(1×2×3×4×5×6×7)×(1×2×4×5×7×8×10×11×13×14×16×17)×(19×20×22)22! = 3^7\times(1\times2\times3\times4\times5\times6\times7) \times(1\times2\times4\times5\times7\times8\times10\times11\times13\times14\times16\times17) \times (19\times20\times22)

第一个部分是 ppnp\displaystyle \lfloor\frac{n}{p}\rfloor 次幂,我们需要记录其指数以计算 rr

第二个部分是 np!\lfloor\dfrac{n}{p}\rfloor!,我们可以递归求解。

第三个部分是 1×2×4×5×7×8×10×11×13×14×16×171\times2\times4\times5\times7\times8\times10\times11\times13\times14\times16\times17,仔细观察可以发现:

1×2×4×5×7×810×11×13×14×16×17(mod32)1\times2\times4\times5\times7\times8 \equiv10\times11\times13\times14\times16\times17\pmod{3^2}

形式化地,有:

i=1,gcd(i,p)=1pqii=1,gcd(i,p)=1pq(i+pqs)(modpq)\prod_{i = 1,\gcd(i, p) = 1}^{p^q} i \equiv \prod_{i = 1,\gcd(i, p) = 1}^{p^q} (i + p^q \cdot s) \pmod{p^q}

(其中 sZs \in \Z。)

这个循环节一共循环了 npq\lfloor\dfrac{n}{p^q}\rfloor 次,我们直接将其求出来,然后再对其求幂即可。

最后是 19×20×2219 \times 20\times 22,形式化的表示即为:

i=1,gcd(i,p)=1nmodpqi\prod_{i = 1,\gcd(i, p) = 1}^{n\bmod p^q} i

我们在求解第三个部分的答案时顺便计算其值即可。


实际上,如果询问次数较多,我们还可以对第三个部分和第四个部分进行预处理,这样单次时间复杂度就仅和 pqp^q 的个数、第一个部分和第二个部分有关了。


阶与原根

在很多和数论相关的证明中,我们都能看到原根的影子。

这是因为原根所具有的优美性质:

在模 pp 意义下,任意一个与 pp 互质的数都能够被表示为 gig^i 的形式(其中 gg 为模 pp 的原根,a[0,φ(p))a\in[0, \varphi(p))),换言之,gig^i 遍历整个模 pp 的缩系。

接下来我们一起来探讨原根所具有的优美性质。

(注意:下文将出现较多的定理,笔者不会对它们进行证明,对证明感兴趣的读者可以参考 OIWiki\mathcal{OI-Wiki}。)


在了解原根之前,我们首先需要了解阶。

apa,p 互质且 p>1p > 1,则使得同余式 an1(modp)a^n \equiv 1\pmod{p} 成立的最小的正整数 nn 被称为 aa 在模 pp 意义下的阶,记为 δp(a)\delta_p(a)

我们不妨先来探讨阶的性质。

首先是两个较为显然的性质:

a,a2,a3,aδp(a)a, a^2, a^3, \cdots a^{\delta_p(a)} 在模 pp 意义下两两不同余。

若存在 nn 使得 an1(modp)a^n \equiv 1\pmod{p},那么有 δp(a)n\delta_p(a) \mid n

两条性质均可根据阶的最小性反证。

接下来是两个与四则运算相关的性质:

pN+p \in \N_+a,bZa, b \in \Z,且 gcd(a,p)=gcd(b,p)=1\gcd(a, p) = \gcd(b, p) = 1,那么 δp(ab)=δp(a)δp(b)\delta_p(a \cdot b) = \delta_p(a) \cdot \delta_p(b)gcd(δp(a),δp(b))=1\gcd(\delta_p(a), \delta_p(b)) = 1 互为充要条件。

pN+p \in \N_+a,iNa, i \in \N,且 gcd(a,p)=1\gcd(a, p) = 1,则有 δp(ai)=δp(a)gcd(δp(a),i)\delta_p(a^i) = \dfrac{\delta_p(a)}{\gcd(\delta_p(a), i)}

两条性质把阶与乘除法运算联系了起来,方便了我们对阶的转化,对定理的证明等都有很大帮助。


原根

若存在 gpg \perp p 使得 δp(g)=φ(p)\delta_p(g) = \varphi(p),则称 gg 为模 pp 的原根。

这个定义能够直接证明上文所提到的 “gig^i 遍历整个模 pp 的缩系” 这个性质。

我们在探究原根前,首先需要知道一个数是否存在原根。

原根存在定理:正整数 nn 存在原根,当且仅当 n=1,2,4,pq,pq2n = 1, 2,4,p^q, p^q\cdot 2,其中 pp 为奇质数,qq 为正整数。

下面我们来挖掘原根的一些性质。

pp 存在原根,其个数为 φ(φ(p))\varphi(\varphi(p))

原根判定定理:设 pp 存在原根,且有 gcd(g,p)=1\gcd(g, p) = 1,那么 gg 为模 pp 的原根的充要条件是,对于 φ(p)\varphi(p) 的每个质因数 dd,都不满足 gφ(p)d1(modp)g^{\frac{\varphi(p)}{d}} \equiv 1 \pmod{p}

其中第二条性质尤为重要,它使我们能够在对 φ(p)\varphi(p) 进行质因数分解后,用单次 O(log2φ(p))\mathcal{O}(\log_2\varphi(p)) 的时间复杂度判定原根。

那么,对于一个存在原根的数 pp,我们该如何求出其所有原根呢?

我们先考虑枚举出 pp 的最小原根 gg。这一步的复杂度基于我国数学家王元于 19591959 年的证明:若 pp 存在原根,那么最小原根是 O(p14)\mathcal{O}(p^{\frac{1}{4}}) 级别的。所以暴力枚举的时间复杂度在可承受范围内。

枚举出最小原根之后,我们可以枚举所有与 φ(p)\varphi(p) 互质的 i[1,φ(p)]i \in [1, \varphi(p)], 那么 gimodpg^i \bmod p 均为模 pp 的原根。该算法的原理可以参考阶的第四条性质,同时该算法的成立也直接证明了 “若 pp 存在原根,其个数为 φ(φ(p))\varphi(\varphi(p))” 这一条行之。


可以发现,原根的性质大多都能为优化和其相关的算法提供帮助,这便是其的优美之处之一。

熟练运用好原根的性质,对数论题(尤其是数论题的证明)有很大帮助。


离散对数

离散对数实际上是一个群论概念,但是在 OI\mathcal{OI} 中,其常见的形式为其在数论中的特例:

给定非负整数 a,b,pa, b, p,要求最小的非负整数 xx,满足 axb(modp)a^x \equiv b \pmod{p}

接下来我们介绍两种用于求解数论中的离散对数的算法。


BSGS 算法

BSGS\mathcal{BSGS} 全称 Baby Step,Giant Step\mathcal{Baby~Step, Giant~Step},又名 “大步小步” 算法,它实际上是基于一个类似分段打表的思想的。

实际上,根据底数 aa 和模数 pp 是否互质,这个算法又会有一些细节上的不同。


底数和模数互质

q=p,x=qrsq = \lfloor\sqrt{p}\rfloor, x = q \cdot r - s,则方程可以化为:

aqrsb(modp)a^{q \cdot r - s} \equiv b \pmod{p}

进一步有:

(aq)rbas(modp)(a^q)^r \equiv b\cdot a^s \pmod{p}

因为 s[0,q)s \in [0, q),所以我们可以考虑枚举 ss,然后把所有 basmodpb \cdot a^s \bmod p 用哈希表存储(如果值有重复,那么选择最大的 ss 即可,这样可以保证答案最小),再从小到大枚举 rr,如果对于当前枚举到的 rr(aq)r(a^q)^r 可以在哈希表中查找到,那么此时的 qrsq \cdot r - s 就是最小的 xx

由于 xxO(p)\mathcal{O}(p) 级别的,因此 rrss 一样,也是 O(p)\mathcal{O}(\sqrt{p}) 级别的,因此算法的时间复杂度为 O(p)\mathcal{O}(\sqrt{p})


底数和模数不互质

注意到此时上述的两个同余方程并非互为充要条件,因此我们需要先对方程做一些转化。

首先特判 b=1b = 1 的情况,此时原方程有解 x=0x = 0

g=gcd(a,p)g = \gcd(a, p),那么根据取模运算的性质,原方程可以转化为:

ax1agbg(modpg)a^{x - 1}\cdot\frac{a}{g} \equiv \frac{b}{g} \pmod{\frac{p}{g}}

显然,若某次转化后有 gbg \nmid b,那么原方程是无解的。

如果保证有解,那么我们可以考虑不断地转化这个方程,直到 api=1kgia \perp \dfrac{p}{\displaystyle \prod_{i = 1}^k g_i}

此时有:(设 t=i=1kagi\displaystyle t = \prod_{i = 1}^k\dfrac{a}{g_i}

axkbti=1kgibak (mod pi=1kgi)\displaystyle a^{x - k} \equiv \dfrac{b}{\displaystyle t \cdot \prod_{i = 1}^k g_i} \equiv \frac{b}{a^k} ~(\bmod~\frac{p}{\displaystyle \prod_{i = 1}^k g_i})

这个时候就可以用普通的 BSGS\mathcal{BSGS} 求解了,最后的答案记得要加上 kk

当然,算法过程中还需要特判 t=bi=1kgi\displaystyle t = \frac{b}{\displaystyle \prod_{i = 1}^k g_i} 的情况,此时 aa 的次数为 00,答案即为 kk


Pohlig-Hellman 算法

这个算法一般用于求解 pp 为质数且 p1p - 1Smooth Number\mathcal{Smooth~Number}(指质因数分解过后每个质因数均较小的数)的情况。但实际上,只要 pp 存在原根且 φ(p)\varphi(p)Smooth Number\mathcal{Smooth~Number},这个算法就是适用的。

下文默认 pp 为质数且且 p1p - 1Smooth Number\mathcal{Smooth~Number}

首先考虑一个模 pp 的原根 gg,设 aga(modp),bgb(modp)a \equiv g^{a'} \pmod{p},b \equiv g^{b'}\pmod{p},那么 axb(modp)a^x \equiv b \pmod{p} 等价于 gaxgb(modp)g^{a'\cdot x} \equiv g^{b'}\pmod{p},也即 axb(modp1)a' \cdot x \equiv b' \pmod{p-1}

所以我们仅需求出 aa'bb',便可以用 exgcd\operatorname{exgcd} 求出 xx 的最小非负整数解。

现在我们聚焦于问题:对于方程 gxa(modp)g^x \equiv a\pmod{p},求出其最小非负整数解。

首先将 p1p - 1 质因数分解,然后考虑对每一个 uvu^v,求出 xmoduvx \bmod{u^v},最后再用中国剩余定理合并。

我们将 xx 表示为 uu 进制,即 x=(i=0v1riui)moduv\displaystyle x = (\sum_{i = 0}^{v - 1}r_i\cdot u^i) \bmod{u^v},那么问题在于如何求出所有的 rir_i

考虑构造方程 (gx)p1usap1us(modpq)(g^x)^{\frac{p - 1}{u^s}}\equiv a^{\frac{p - 1}{u^s}} \pmod{p^q},然后将里面的 xx 展开可得 (gr0+r1u+r2u2++rv1uv1)p1usap1us(modpq)(g^{r_0 + r_1 \cdot u + r_2 \cdot u^2 +\cdots + r_{v - 1}\cdot u^{v - 1}})^{\frac{p - 1}{u^s}}\equiv a^{\frac{p - 1}{u^s}} \pmod{p^q}

若我们令 s=1s = 1,那么原方程可以转化为 gr0(p1)ugr1(p1)gr2u(p1)grv1uv2(p1)ap1us(modpq)g^{\frac{r_0 \cdot (p - 1)}{u}} \cdot g^{r_1\cdot ( p - 1)} \cdot g^{r_2\cdot u \cdot ( p - 1)} \cdot \cdots \cdot g^{r_{v - 1}\cdot u^{v - 2} \cdot (p - 1)}\equiv a^{\frac{p - 1}{u^s}} \pmod{p^q},根据费马小定理,我们还可以将其转化为 gr0(p1)uap1us(modpq)g^{\frac{r_0 \cdot (p - 1)}{u}}\equiv a^{\frac{p - 1}{u^s}} \pmod{p^q},到了这一步,我们直接枚举 r0r_0,或是用 BSGS\mathcal{BSGS} 算法求解 r0r_0 均可。

接下来我们再令 s=2vs = 2 \sim v,分别求出 r1v1r_{1\sim v - 1} 的值,即可求出 xx 的值。

显然时间复杂度和 uu 的大小有关,因此本算法适用于 p1p - 1Smooth Number\mathcal{Smooth~Number} 的情况。


此处有一个小问题:为什么一定先把 axb(modp)a^x \equiv b\pmod{p} 转化为 aga(modp)a \equiv g^{a'} \pmod{p}bgb(modp)b \equiv g^{b'}\pmod{p} 再求解呢?不能直接将其套用上文的算法求解吗?

实际上,在上文的算法中,我们利用到了 δp(g)=p1\delta_p(g) = p - 1 这一条性质。如果 δp(g)<p1\delta_p(g) < p - 1,那么对于上文中的方程 gr0(p1)uap1us(modpq)g^{\frac{r_0 \cdot (p - 1)}{u}}\equiv a^{\frac{p - 1}{u^s}} \pmod{p^q},若存在 gp1u1(modpq)g^{\frac{p - 1}{u}}\equiv 1 \pmod{p^q},那么我们根本无法求出 a0a_0


二次剩余

对于正整数 nn,若 x2n(modp)x ^ 2 \equiv n \pmod{p} 有解,则称 nn 为模 pp 的二次剩余,否则为模 pp 的二次非剩余。

下文我们仅讨论 pp 为奇质数的情况。对于其它的情况,笔者并不了解相关算法,因此略过不谈。

注意当 n=0n = 0 时方程必然有解,此时我们认为 00 是模 pp 的二次剩余,但下文提到 “二次剩余” 时,我们默认不考虑这种情况。


解的情况

我们先来讨论一下解的情况。

设方程有两个不等的解,分别为 x1,x2x_1,x_2,则有 x12x22(modp)x_1^2 \equiv x_2^2\pmod{p},移项后有 (x1x2)(x1+x2)0(modp)(x_1 - x_2) \cdot (x_1 + x_2) \equiv 0\pmod{p},而由于前者必然不等于 00,所以 x1x_1x2x_2 在模 pp 意义下必为相反数,并且由于 pp 为奇质数,因此它们是互不相等的。

也就是说,方程最多有两互异解,并且它们互为相反数。同时,由于我们不考虑 n=0n = 0 的情况,因此有解的方程一定有恰好两互为相反数的解。

进一步地,由于一对相反数仅会对应一个二次剩余,那么二次剩余的个数为 p12\dfrac{p - 1}{2},其它的数即为二次非剩余,二者的数量相等。


Legendre 符号与 Euler 判别准则

Legendre\mathcal{Legendre} 符号记作 (ap)\displaystyle \left(\frac{a}{p}\right)。若其等于 11,则说明 aa 为模 pp 的二次剩余;若其等于 1-1,则说明 aa 为模 pp 的二次非剩余;若其等于 00,则说明 aa 在模 pp 意义下与 00 同余。

值得一提的是,Legendre\mathcal{Legendre} 符号还有更多的性质,比如它实际上是一个完全积性函数。感兴趣的读者可以自行查阅相关资料。

至于如何对 Legendre\mathcal{Legendre} 符号求值,对于 a0(modp)a \equiv 0 \pmod{p} 的情况我们无需在意,而对于剩下的情况,我们需要判别 aa 是否为模 pp 的二次剩余。

Euler\mathcal{Euler} 判别准则就是因此而生的:

xp121(modp)x ^ {\frac{p - 1}{2}} \equiv 1\pmod{p} 成立时,xx 为模 pp 的二次剩余,否则为二次非剩余,即 “xp121(modp)x ^ {\frac{p - 1}{2}} \equiv 1\pmod{p} 成立” 是 “xx 是模 pp 的二次剩余” 的充要条件。

证明:

先证必要性,设 xy2(modp)x \equiv y ^ 2 \pmod{p},由费马小定理可知 xp12yp11(modp)x ^ {\frac{p - 1}{2}} \equiv y ^ {p - 1} \equiv 1 \pmod{p}

再证充分性,设 gg 为模 pp 的原根,那么存在 ii 使得 xgi(modp)x \equiv g^i \pmod{p},又因为 xp12gi(p1)21(modp)x^{\frac{p - 1}{2}} \equiv g^{\frac{i\cdot (p - 1)}{2}} \equiv 1 \pmod{p},根据阶的性质,i(p1)2\dfrac{i\cdot(p - 1)}{2} 一定是 p1p - 1 的倍数,所以 ii 是偶数,令 m=gi2m = g^{\frac{i}{2}} 就有 xm2(modp)x \equiv m^2 \pmod{p}

综上,我们仅需判断 xp121(modp)x ^ {\frac{p - 1}{2}} \equiv 1\pmod{p} 是否成立,就可以知道 xx 是否为二次剩余。


Cipolla 算法

判定了 nn 是否为模 pp 的二次剩余后,我们怎样求出方程的解呢?

下面笔者将介绍 Cipolla\mathcal{Cipolla} 算法。

对于方程 x2n(modp)x^2 \equiv n \pmod{p},我们先随机出一个 yy 使得 y2ny ^ 2 - n 为二次非剩余,由于其个数与二次剩余相等,因此期望 22 次就能找到。

然后我们类似虚数,将数域扩充,定义 i2y2n(modp)i ^ 2 \equiv y ^ 2 - n \pmod{p}

接下来给出结论:x=(y+i)p+12x = (y + i) ^ {\frac{p + 1}{2}} 为原方程的一个解。

证明:

首先我们可以通过 Lucas\mathcal{Lucas} 定理证明 (y+i)pyp+ip(modp)(y + i) ^ p \equiv y^p + i ^ p \pmod{p}

其次由费马小定理可知 ypy(modp)y ^ p \equiv y \pmod{p}

然后根据 Euler\mathcal{Euler} 判别准则和二次探测定理可知二次非剩余 xx 满足 xp121(modp)x^{\frac{p - 1}{2}} \equiv -1 \pmod{p},那么有 (i2)p12ip11(modp)(i ^ 2) ^ {\frac{p - 1}{2}} \equiv i ^ {p - 1} \equiv -1\pmod{p},即 ipi(modp)i ^ p \equiv -i \pmod{p}

所以:

x2(y+i)p+1(y+i)p(y+i)(yp+ip)(y+i)(yi)(y+i)=y2i2=n\begin{aligned} x ^ 2 &\equiv (y + i)^{p + 1} \\ & \equiv (y + i) ^ p \cdot (y + i) \\ & \equiv (y ^ p + i ^ p)\cdot (y + i) \\ & \equiv (y - i)\cdot (y + i) \\ & = y ^ 2 - i ^ 2 = n \end{aligned}

我们求出 xx 之后,再通过上文中的结论求出另外一个解即可。


Miller-Rabin 质数判别算法

对于正整数 nn,我们都知道存在时间复杂度为 O(n)\mathcal{O}(\sqrt{n})O(n)O(nlnn)\displaystyle\mathcal{O}(\sqrt{n}) - \mathcal{O}(\frac{\sqrt{n}}{\ln n})的判别其是否为质数的算法,但是对于较大的 nn,这个算法的时间复杂度还是比较难以接受的。

是否有复杂度更优秀的算法呢?


费马测试

我们不妨考虑质数的性质,其中有一条名为费马小定理:若 pp 是质数,则对于所有 x[1,p)x \in [1, p) 都有 xp11(modp)x^{p-1} \equiv 1 \pmod{p}

那么,我们是否可以通过随机一些 xx,判断上述条件是否对于这些 xx 均成立,从而判断 pp 是否为质数呢?

很遗憾,不行,原因是存在卡迈克尔数,对于这些数,我们无论如何都会将它们判别为质数。虽然它们的数量很少,但要预处理并特判它们也并不是简单的。


二次探测定理

我们不妨再来考虑质数的性质,其中有一条名为二次探测定理:若 pp 是质数,且有 x21(modp)x^2 \equiv 1 \pmod{p},则 x=1x = 1x=p1x = p - 1

那么,我们是否可以通过将 p1p - 1 分解为 q2rq \cdot 2^r 的形式,然后随机一些 xx,依次判断是否有 xq2s+11(modp)x^{q \cdot 2^{s + 1}} \equiv 1 \pmod{p}xq2s1 or p1(modp)x^{q \cdot 2^s} \equiv 1~\texttt{or}~p -1 \pmod{p},最后再判断是否有 xp11(modp)x^{p -1}\equiv 1 \pmod{p},从而判断 pp 是否为质数呢?

很幸运,这是可以的,在 xx 随机的情况下,这个算法被证明有极高的正确率。事实上,参考这里这里,我们可以通过调整测试数以得到确定性的判断结果,这样我们就无须担心算法的正确率,而仅需考虑一些需要特判的情况了。


最后,这个算法是可以做到单次测试 O(log2n)\mathcal{O}(\log_2n) 的时间复杂度的,读者不妨自行思考如何实现。

需要代码实现的读者可以看这里


Pollard-Rho 因数分解算法

对于正整数 nn,我们还是都知道存在时间复杂度为 O(n)\mathcal{O}(\sqrt{n})O(n)O(log2n)\mathcal{O}(n) - \mathcal{O}(\log_2n)O(n)O(nlnn)\displaystyle \mathcal{O}(\sqrt{n}) - \mathcal{O}(\frac{\sqrt{n}}{\ln n}) 的对其进行质因数分解的算法,但是对于较大的 nn,这些算法的时间复杂度还是比较难以接受的。

是有有复杂度更优秀的算法呢?


确定方向

MillerRabin\mathcal{Miller-Rabin} 算法,我们可以考虑在非确定性算法上寻找出路:我们可以通过随机化算法找出 nn 的一个非平凡因子 mm,即满足 m1 or nm \ne 1~\texttt{or}~nmnm \mid n。这样,若找出 pp 的复杂度为 O(f(n))\mathcal{O}(f(n)),那么算法的总时间复杂度为 O(f(n)log2n)\mathcal{O}(f(n)\log_2n)

直接随机显然是不行的,我们考虑一种更为优秀的随机化算法。


生日悖论

我们来思考这样一个问题:不考虑出生年份,并假设每年均为 365365 天,那么当一个房间内有至少多少人时,有两个人生日相同的概率能够达到 50%50\%

直觉告诉我们答案应该为 183183 人,但如果我们列出不等式,便可以得到一个十分反直觉的正确答案:2323 人。这也就是为什么该数学事实被称为一个悖论。

注意到 2323 是一个 365365 的平方根级别的数。实际上,根据生日悖论,我们可以导出一个推论:若我们在 [1,n][1, n] 的范围内随机生成一个数并将其加入序列中,那么当序列中第一次出现两个相等的数时,序列的长度是期望 O(n)\mathcal{O}(\sqrt{n}) 级别的。

这个推论将在下文起到非常重要的作用。


构造伪随机函数

我们考虑构造这样一个伪随机函数:f(x)=(x2+c)modnf(x) = (x^2 + c)\bmod n,其中 cc 是随机的常数。

这个函数有什么性质呢?

我们随机一个初值 ss,然后观察序列 s,f(s),f(f(s)),s, f(s), f(f(s)), \cdots,可以发现,这个函数会从某个位置开始进入一个循环。如果将有向图模型建出,可以发现其和 ρ\rho 的形状相类似,这便是这个算法的名字的由来。

设上述序列为 xix_i,再设序列 yiy_i,且有 yi=ximodmy_i = x_i \bmod m(其中 mmnn 的最小非平凡因子),可以发现 yi+1=(yi2+c)modmy_{i +1} = (y_i^2 +c) \bmod m 仍然成立,也即 yiy_i 的有向图模型同样和 ρ\rho 的形状相类似。

注意到若存在一对位置 i,ji, j,满足 xi≢xj(modn)x_i \not\equiv x_j \pmod{n}yiyj(modm)y_i \equiv y_j \pmod{m},则有 nxixjn \nmid |x_i - x_j|mxixjm \mid |x_i - x_j|,那么此时 gcd(n,xixj)\gcd(n, |x_i - x_j|) 即为 nn 的一个非平凡因子。

而根据上文生日悖论的推论,ij|i - j|(也即 yiy_iρ\rho 形图的环长) 期望是 O(m)O(n14)\mathcal{O}(\sqrt{m})\approx \mathcal{O}(n^{\frac{1}{4}}) 级别的,那么,如果我们能够做到在 O(ij)\mathcal{O}(|i - j|) 的时间复杂度内找到 nn 的一个非平凡因子,我们就得到了一个复杂度十分优秀的算法。


算法流程

接下来我们考虑如何实现这个算法。

我们可以设置两个值 uuvv,分别从初值 ss 开始,每次令 vv 等于 f(v)f(v),然后判断 gcd(n,uv)\gcd(n, |u - v|) 是否为一个非平凡因子。

然而这个算法存在两个问题:其一是 uu 可能不在 yiy_iρ\rho 形图的环上,其二是有可能我们遍历了整个 xix_iρ\rho 形图的环都无法成功地找到一个非平凡因子。

这两个问题均可以用 Floyd\mathcal{Floyd} 判环法来解决。具体地,我们还是设置两个值 uuvv,分别从初值 ss 开始,每次我们令 uu 等于 f(u)f(u),令 vv 等于 f(f(v))f(f(v))

这样,我们期望在 O(m)\mathcal{O}(\sqrt{m}) 次后遍历整个 xix_iρ\rho 形图的环,此时要么我们已经找到了一个非平凡因子,要么我们需要重新执行一遍上述算法。

这个算法的时间复杂度已经很优秀了,但其中包含了较多次数的 gcd\gcd 运算,这实际上会大大拖慢程序的运行速度。

根据相关性质,我们可以考虑把一部分的 uv|u - v| 乘在一起,即令 mul=(uv)modn\displaystyle mul = (\prod |u - v|) \bmod n,然后判断 gcd(n,mul)\gcd(n, mul) 是否为一个非平凡因子。具体地,我们可以考虑倍增 ij|i - j| 的范围,然后将对应范围内的 uv|u - v| 乘在一起,经实测当倍增到 128128 时停止倍增有最优的运行效率。

这样,我们可以把 gcd\gcd 运算的次数看做一个常数,那么算法的期望时间复杂度即为 O(m)\mathcal{O}(\sqrt{m})


实际上,PollardRho\mathcal{Pollard-Rho} 算法的复杂度并没有得到十分严谨的证明,其原因是伪随机函数是否足够均匀还有待商榷。但不可否认的是,算法的运行效率确实非常高,这也让它能够很好地胜任大整数分解的任务。

需要代码实现的读者可以看这里


积性函数的亚线性筛法

我们都知道埃氏筛的时间复杂度是近线性的,欧拉筛的时间复杂度是线性的,二者均可以筛出范围内的所有函数值。

但如果我们仅需求出函数值的前缀和,并且 nn 的范围能够达到 10910^9 甚至更高的级别,那么上述两种筛法就不太适用了。

接下来笔者将会介绍三种筛法,它们能够做到在亚线性的时间复杂度内求出积性函数的前缀和。


杜教筛

假设现在我们要求 f(n)f(n) 的前缀和,即 S(n)=i=1nf(i)\displaystyle S(n) = \sum_{i = 1} ^n f(i)

我们考虑找出一个函数 g(n)g(n),把 f(n)f(n)g(n)g(n) 做狄利克雷卷积,设其前缀和为 S(n)S'(n),那么有:

S(n)=i=1n(fg)(i)=i=1njif(j)g(ij)=i=1ng(i)i=jnif(j)=i=1ng(i)S(ni)\begin{aligned} S'(n) &= \sum_{i = 1}^n(f \cdot g)(i) \\ &= \sum_{i = 1} ^n \sum_{j | i} f(j) \cdot g(\frac{i}{j}) \\ &=\sum_{i = 1}^n g(i) \sum_{i = j}^{\lfloor\frac{n}{i}\rfloor} f(j) \\ &=\sum_{i = 1}^ng(i)\cdot S(\lfloor\frac{n}{i}\rfloor) \end{aligned}

进一步地,有:

g(1)S(n)=i=1ng(i)S(ni)i=2ng(i)S(ni)g(1)\cdot S(n) = \sum_{i = 1}^ng(i)\cdot S(\lfloor\frac{n}{i}\rfloor) - \sum_{i = 2}^ng(i)\cdot S(\lfloor\frac{n}{i}\rfloor)

即:

S(n)=S(n)i=2ng(i)S(ni)g(1)S(n) = \frac{\displaystyle S'(n)- \sum_{i = 2}^ng(i)\cdot S(\lfloor\frac{n}{i}\rfloor)}{g(1)}

所以,只要找出合适的 g(n)g(n),就能够快速求出 S(n)S(n) 了。

举两个例子:

对于 μ(n)\mu(n) 的前缀和,我们发现有 μ1=ϵ\mu * 1 = \epsilon,即设 g(n)=1g(n) = 1,代入公式有 S(n)=1i=2nS(ni)\displaystyle S(n) = 1 - \sum_{i = 2}^nS(\lfloor\frac{n}{i}\rfloor)

对于 φ(n)\varphi(n) 的前缀和,我们发现有 φ1=id\varphi \cdot 1 = \operatorname{id},即设 g(n)=1g(n) = 1,代入公式有 S(n)=n(n+1)2i=2nS(ni)\displaystyle S(n) = \frac{n(n + 1)}{2} - \sum_{i = 2}^n S(\lfloor\frac{n}{i}\rfloor)

这些递推式显然可以整除分块做。

实际上,我们可以先预处理出 n23n^{\frac{2}{3}} 以内的 S(n)S(n),对于剩下的 S(n)S(n) 用记忆化搜索处理。可以证明,这样可以做到最优的时间复杂度 O(n23)\mathcal{O}(n^{\frac{2}{3}})


Min_25 筛

这种筛法对于 f(n)f(n) 有一定的要求:当 nn 为质数时,f(n)f(n) 是一个关于 nn 的度数较小的多项式。

首先考虑求质数的前缀和:

i=1,iPrimenf(i)\sum_{i = 1,i \in \mathcal{Prime}}^n f(i)

我们进行这样一个转化:考虑当 nn 为质数时 f(n)f(n) 的运算公式,然后把所有数都看作质数并套用这个运算公式。不妨设这个新的函数为 h(n)h(n)

前面提到,当 nn 为质数时,f(n)f(n) 是一个度数较小的多项式。所以,我们可以把 h(n)h(n) 拆成若干项,分开来算,最后将它们合并在一起即可。为什么要将其拆成若干项?因为若我们不考虑符号和系数,那么多项式的每一项都是完全积性函数,这将为接下来的运算提供很大的帮助。

g(n,j)g(n, j) 为所有质数和 1n1\sim n 中最小质因子大于第 jj 个质数的合数的 h(n)h(n) 的和,并设 pjp_j 为第 jj 个质数。

g(n,0)g(n, 0) 是很好求的,套用公式或用拉格朗日差值均可,那么问题在于如何转移。

转移的基本思路是按照 jj 从小往大转移。

pj>np_j > \sqrt{n},此时不会再有更多的合数对值造成影响,因此有 g(n,j)=g(n,j1)g(n, j) = g(n, j - 1)

否则有 pjnp_j \le \sqrt{n},考虑需要被减去的贡献,显然这些数一定含有质因子 pjp_j,所以我们需要减去 h(pj)g(npj,j1)\displaystyle h(p_j)\cdot g(\lfloor\frac{n}{p_j}\rfloor,j−1),但 g(npj,j1)\displaystyle g(\lfloor\frac{n}{p_j}\rfloor,j−1) 中包含了 i=1j1h(pi)\displaystyle \sum_{i=1}^{j - 1}h(p_i) 这一部分小于 pjp_j 的质数的函数值,它们实际上不应被减去(因为 pipjp_i\cdot p_j 的最小质因子为 pip_i),所以再把它们加上就好了。

总结可得递推式:

g(n,j)=g(n,j1) (pj>n)g(n,j)=g(n,j1)h(pj)(g(npj,j1)i=1j1h(pi)) (pjn)\begin{aligned} g(n, j) &= g(n, j - 1)~(p_j > \sqrt{n}) \\ g(n, j) &= g(n, j - 1) - h(p_j)\cdot (g(\lfloor\frac{n}{p_j}\rfloor,j - 1) - \sum_{i=1}^{j - 1}h(p_i))~(p_j \le \sqrt{n}) \end{aligned}

求出来的 g(n,Prime)g(n, |\mathcal{Prime}|) 即为 1n1 \sim n 内所有质数的函数值之和。

接下来,类⽐ g(n,j)g(n, j),设 S(n,j)S(n, j) 表⽰最⼩质因⼦大于第 jj 个质数的所有数的 f(n)f(n) 的和。

对于质数的部分,答案即为 g(n,Prime)i=1jf(pi)\displaystyle g(n, |\mathcal{Prime}|) - \sum_{i = 1}^{j} f(p_i),那么我们仅需考虑合数的部分。

枚举合数时,我们需要考虑所有最小质因子为 q (q>pj)q~(q > p_j) 的数 rr 对应的 f(r)f(r) 的贡献,而显然合数 rr 的最小质因子是不超过 r\sqrt{r} 的,所以我们仅需考虑满足 q2nq^2 \le n 的部分即可。

对于每一个质因子 qq,我们还需考虑其指数 hh。由于 S(n,j)S(n, j) 中实际上不包含 f(1)f(1) 的贡献,那么我们需要先加入 f(qh)f(q^h) 这一部分贡献(注意这里不能重复加入质数的贡献)。对于剩下的部分,我们类似 g(n,j)g(n, j) 的处理,利用积性函数的性质将 f(qh)f(q^h) 这一项提出来,再递归求解所有最小质因子大于 qqS(n,j)S(n, j) 即可。

总结可得递推式:

S(n,j)=g(n,Prime)i=1jf(pi)+k=j+1pk2nh=1pkhn(f(pkh+1)+f(pkh)S(npkh,k+1))S(n, j) = g(n, |\mathcal{Prime}|) - \sum_{i = 1}^{j} f(p_i) + \sum_{k = j + 1}^{p_k^2 \le n}\sum_{h = 1}^{p_k^{h} \le n} (f(p_k^{h + 1}) + f(p_k^{h}) \cdot S(\lfloor\frac{n}{p_k^h}\rfloor, k + 1))

(注意当 pkh+1p_k^{h + 1} 大于 nn 时,我们认为 f(pkh+1)f(p_k^{h + 1})00。)

最后得到的 S(n,0)S(n, 0) 即为答案,不过还需要加上 f(1)f(1) 的贡献。

具体到代码实现上还有较多的细节,建议读者参考网上其它大佬的代码实现。


Powerful Number 筛

对正整数 nn 质因数分解为 piPrimepiqi\displaystyle\sum_{p_i\in \mathcal{Prime}} p_i^{q_i},如果对于所有的 ii,有 qi2q_i \ge 2,则称 nnPowerful Number\mathcal{Powerful~Number}

实际上我们可以证明:1n1 \sim nPowerful Number\mathcal{Powerful~Number} 的数量在 O(n)\mathcal{O}(\sqrt{n}) 级别。而这一条性质可以保证 Powerful Number\mathcal{Powerful~Number} 筛的复杂度。

假设我们要求积性函数 f(n)f(n) 的前缀和,那么我们需要找出一个函数 g(n)g(n),满足其前缀和很好求,且两函数在质数处值相等,再找出一个积性函数 h(n)h(n) 使得 f(n)=h(n)g(n)f(n) = h(n) \cdot g(n)

可以发现,当 nn 不为 Powerful Number\mathcal{Powerful~Number} 时,h(n)h(n) 取值为 00

又有:

i=1nf(i)=i=1n(hg)(i)=i=1njih(j)g(ij)=i=1nh(i)j=1nig(j)\begin{aligned} \sum_{i = 1}^n f(i) &= \sum_{i = 1}^n (h \cdot g)(i) \\ &=\sum_{i = 1}^n\sum_{j | i} h(j)\cdot g(\frac{i}{j}) \\ &=\sum_{i = 1}^nh(i)\sum_{j = 1}^{\lfloor\frac{n}{i}\rfloor} g(j) \end{aligned}

由于 g(n)g(n) 的前缀和很好求,因此我们暴力地找出 nn 以内的 Powerful Number\mathcal{Powerful~Number},算出对应的 h(n)h(n) 之后再将它们相加即可。


本文没有讲解这些筛法的时间复杂度及其证明,对这些内容感兴趣的读者可以参考 20182018 年朱震霆的国家集训队论文《一些特殊的数论函数求和问题》。


未完待续 ……