虽说比较困难,但是代码实现十分简单。
1. 定义
我们求解 $x ^ 2 \equiv n \pmod p$,其中 $n, p$ 给定,$p$ 是质数。就是二次剩余,可以记为 $x = \sqrt n$。
一般使用 Cipolla 算法,时间复杂度是玄学,期望是 $O(\log n)$,一般也比较稳定。
2. 推导过程
首先,证明一下在 $\bmod p$ 意义下的有 $\sqrt n$ 的 $n$ 的个数有 $\dfrac{p - 1}{2}$ 个。
首先,我们容易发现,$x$ 与 $p - x$ 在 $\bmod p$ 意义下是相等的。于是我们只需要判断 $x\in[1, \dfrac{p - 1}{2}]$ 的 $x ^ 2$ 是不相同的。这样的话,对于 $\dfrac{p - 1}{2}$ 个 $n$ 都有两个不同的二次剩余对应它。
使用反证法,我们假设 $x_1,x_2\in[1, \dfrac{p - 1}{2}]$ 的平方是一样的,那么可以得到 $(x_2 +x_1)(x_2 - x_1)\equiv 0\pmod p$。但是 $x_2 + x_1$ 和 $x_2 - x_1$ 都是不可能相等的,所以不可能存在这样的 $x_1$ 和 $x_2$。
第二个,我们有一个定理:如果 $n^{\frac{p - 1}{2}}\equiv 1\pmod p$,那么 $n$ 在 $\bmod p$ 下是有二次剩余的。
这一个比较难证,这里就略过了。但是一个可以明确的地方是 $\forall n, n^{\frac{p - 1}2}\bmod p\in \{1, p - 1\}$。因为 $n^{p - 1}\bmod p = 1$。
第三个,我们找到一个 $a$,使得 $a^2 - n$ 是一个非二次剩余。找到 $a$ 的话,我们使用随机化算法,在 $[0, p - 1]$ 中随机,然后判断 $(a ^ 2 - n)^{\frac{p - 1}2}$ 是否等于 $-1\bmod p$。由于有 $\dfrac{p - 1}{2}$ 个数不是二次剩余,所以我们期望 2 次就可以找到一个 $a ^ 2 - n$。这一步的期望是 $O(\log n)$。
设 $w = a ^ 2 - n$。
最后一步,我们定义一个二维向量 $(a, b)$,表示 $a + b\sqrt w$。
我们可以得到第三个结论:将二维向量当作一个数来乘,$\sqrt n = (a, 1) ^{\frac{p - 1}{2}}$。
其中,$(a, 1)$ 表示 $a + \sqrt w$ 这个数,我们将这个数 $\dfrac{p - 1}{2}$ 次方,实部就是 $\sqrt n$。
证明也太难了,我们就不讲了。
3. 代码实现
首先,我们将向量直接看做复数封装好。
1 | struct Complex{ |
其中 qpow_com
表示复数快速幂,其实是比较简单的。
然后就可以直接实现了。
1 | ll calc(ll n) |
完全版。
详细代码
1 | class Cipolla{ |