数据结构与算法: 素数查找问题

Primality Testing

Posted by R1NG on April 2, 2022 Viewed Times

素数检测问题

在本章中, 我们考虑 寻找素数 的问题. 由 素数定理 (Prime Number Theorem):

记 $\pi(n)$ 为 数值小于等于 $n$ 的所有数中素数的个数, 则对任意 $n$, 有:

\[\lim_{n \rightarrow \infty} \frac{\pi(n)}{\frac{n}{\ln(n)}} = 1.\]

也就是说, $\frac{n}{\ln(n)}$ 给出了 小于等于 $n$ 的素数个数近似.

因此可知, 考虑一个 足够大 的数 $n$, 则在前 $n$ 个数中有

\[\pi(n) \approx \frac{n}{\ln(n)}\]

个素数.

因此, 在前 $n$ 个数中 随机抽取一个, 得到的数确实是素数的概率为

\[\frac{\pi(n)}{n} = \frac{1}{\ln(n)}.\]

比如, 如果我们需要找到一个二进制位长为 $1024$ 的整数, 则需要检查 $\ln(2^{1024}) \approx 710$ 次.

素数定理告诉我们, 寻找素数问题等价于 检测某个范围内的随机数是否为素数 的问题.

素数检测的基本方法与核心思想

下面讨论检测素数的核心思想与基本方法: 暴力测试费马小定理, 并进一步解释为何 伪素数的存在决定我们无法直接使用费马小定理检测素数.

从定义出发, 素数是只被自身和 $1$ 整除的数. 并且我们知道, 考虑数 $n$, 由于检查大于 $\sqrt{n}$ 且小于 $n$ 的数是否整除 $n$ 实际上就是在检查位于 $1$ 到 $\sqrt{n}$ 之间的数能否整除 $n$, 因此有:

暴力测试

最基本的 暴力测试 (Trial Division) 就是在 $1$ 到 $\sqrt{n}$ 之间的数字中挨个检查它们能否整除 $n$ (当然, 除了 $1$ 以外). 如果它们中只要有一个能整除 (当然, $1$ 例外), 就说明被测数 $n$ 不为素数:

20220509231912

费马小定理和伪素数 (Carmichael Number)

进一步地, 我们还知道 费马小定理 的结论:

对任意整数 $x$ 和素数 $p$, 若 $x ~\text{mod}~p \neq 0$, 则均满足

\[x^{p-1} ~\equiv~ 1 ~ (\text{mod} ~ p)\]

因此看上去, 利用时间复杂度为 线性快速模幂算法 随机挑选满足条件的整数 $x$, 测试 $x^{n-1}$ 即可检测被测数 $n$ 是否为素数.

20220509232159

然而不幸的是, 满足费马小定理只是一个数 $n$ 为素数的 必要条件, 一类被称为 Carmichael Numbers (伪素数) 的合数 (非素数) 同样满足费马小定理:

20220509232313

因此, 由于伪素数的存在, 我们无法直接应用费马小定理检测某个数 $n$ 是否为素数.

随机素数判定法

下面介绍 随机素数判定法 (Randomized Primality Testing):

基于上面一小节的描述, 我们知道满足费马小定理的数 可能是素数, 额可能是 Carmichael 伪素数. 由于在现实中 小概率事件几乎不可能发生, 因此只要我们找到一种 出错概率可控 的素数判断函数, 将 它的出错概率控制在足够小的水平上, 那么我们就可以放心地将它应用到实践中.

考虑一个这样的函数, 记为 $\text{witness}(x, n)$, 用于 判定一个数是否为合数:

  1. 若 $n$ 为素数则该函数返回 false
  2. 若 $n$ 不为素数, 则该函数有 $q < 1$ 的概率也返回 false

称其为 见证函数:

20220511094408

下面结合见证函数 \text{witness}() 介绍 随机素数判定法 (Randomized Primality Testing Algorithm):

20220511133117

其中, $n$ 为 需要被检测的数, $k$ 称为置信度, 表明我们希望以多高的确定率 (准确率) 进行素数判定.

注意此处 $t$ 为执行随机测试的次数, 每一次测试中我们都会生成一个随机数 $x$ 作为见证函数的另一个输入.

如果在所有的随机测试中 $\text{witness}(x)$ 均判定数 $n$ 为素数, 则 在置信度为 $k$ 的前提下, 可判定 $n$ 为一个素数.

下面说明 $t$ 如此取值的原因.

首先进一步解释 置信度 $k$, 它实际上约束的是 判定函数出错的概率. 判定函数出错的概率需要为

\[2^{-k}.\]

而函数整体返回 false 的概率客观上是 $q^{t}$, 因为在这一情况下任何一次检测中见证函数都需要返回 false.

因此目标是

\[q^{t} \leqslant 2^{-k}\]

因此有

\[t \leqslant \log_{q}(2^{-k}) = \frac{k}{-\log_{2}(q)} = \frac{k}{\log_{2}(\frac{1}{q})}.\]

由此计算出 $t$ 的上界. 因此我们有:

20220511170104

Rabin-Miller 素数判定法

最后介绍 Rabin-Miller 素数判定法:

Rabin-Miller 素数判定法 实际上就是一种特殊的随机素数判定法. 我们首先介绍它的见证函数.

回顾定义可知, 见证函数 $\text{witness}(x, n)$ 是 接受随机输入 $x$, 检测某个数 $n$ 是否为素数 的函数, 若 $n$ 为素数的话返回 false, 否则以 $1-q$ 的概率返回 true.

因此, 基本思路是基于 费马小定理, 检查 $x^{n-1} ~\text{mod}~(n) ~\equiv~ 1$ 是否成立, 并痛苦地接受费马小定理误报 Carmichael Number 伪素数为素数的特殊情况.

我們同時有以下結論 (二次探測定理):

若 $p$ 为素数 $x$ 為 任意整數, 則如果有

\[x^2 ~\text{mod}~p~\equiv 1\]

\[x ~\equiv~ 1 ~ \text{mod} ~p\]

\[x ~\equiv~ -1 ~ \text{mod} ~p\]

若 $0 < x < p$, 则解必為 $x=1$ 或 $x = p-1$.


进一步地, 基于数分解知识可知:

若 $n$ 为 奇数, 则 $n-1$ 可表示为:

\[n-1 = 2^k \cdot m\]

其中 $m$ 为某个不可再被 $2$ 分解的奇数.

由于即是偶数又是素数的只有 $2$, 因此在绝大多数情况下我见证函数的检查对象只会是奇数. 故在利用费马小定理进行素数检测时, 可以将

\[x^{n-1} ~\text{mod}~ n\]

表示为

\[x^{2^k \cdot m} ~\text{mod}~ n\]

其中 $2^k \cdot m = n-1$, 然后我们可以依次检测数列

\[x^{2^k \cdot m}, x^{2^{k-1} \cdot m}, \cdots, x^{m}.\]

若:

  1. 若 $n$ 可能为素数, 则對上述的序列中的 $x^{2i \cdot m}, i=k, k-1, …, m$ 取關於 $n$ 的餘數, 第一個必然為 $1$, 而剩下的序列中 第一個不為 $1$ 的數必然是 $-1$.

    20220511222037

  2. 若 $n$ 为合数, 则从 $x^{m}$ 反推, 自 $k=0$ 开始到某个 $k=t$ 为止模运算结果都不为 $\pm 1$, 而 $k > t$ 时模运算结果均为 $1$ 或都不为 $1$.

    (換言之, 對於 $n$ 為合數的情形, 從序列開頭 $x^{2k \cdot m}$ 開始取於數, 要麼 一個 $1$ 都沒有, 要麼是一串 $1$ 後面緊跟著第一個數不為 $-1$.)

    20220511222408

也就是:

20220511222427

这样就完成了一种见证函数, 也就是 Rabin-Miller 算法 的实现.

下面讨论该见证函数的错误率:

20220511222508

也就是說, Rabin-Miller 算法的錯誤率最多為 $\frac{1}{4}$. 因此對於給定的顯著水平 $k$, 若將 Rabin-Miller 函數作為隨機素數判定法的 見證函數, 則知循環次數 $t$ 為:

\[t = \lceil \frac{k}{\log_{2}(\frac{1}{q})} \rceil = \lceil \frac{k}{2} \rceil.\]

最后给出 Rabin-Miller 算法的 时间复杂度:

20220511222709

該算法的時間複雜度是 $O(k \cdot \log(n)).$

相关习题解析

见笔记 “数据结构与算法: 复习”.