判定素数的方法啊……答案取决于很重要的两个条件:“需要判断的数有多大”,以及“这个数有没有特殊形式”。以下我会按照适用范围从小到大来盘点一下现有的判定算法。
注:本文的脉络参考了The Prime Pages上的介绍:https://primes.utm.edu/prove/index.html写在前面
当问题的输入是一个正整数
的时候,一般认为输入规模是
这是因为我们实际的输入是
的二进制展开式。后文中提到“多项式算法”的时候,指的就是运行时间是
的多项式的算法。
另外文中提到的绝大多数算法的主要部分都是大量模
的乘法;文中提到的时间复杂度都假设我们用原始的
的方式做这个乘法。如果
很大,那实际计算中就要用到更优化的乘法方式(Karatsuba,Toom-Cook,包括FFT),整个算法的时间复杂度也要相应的作调整。
naive的试除法
适用范围:
很小,比如32位整数变量什么的
对特殊形式的要求:无
时间复杂度:
普通的试除应该不用多加介绍了,从2试到
就可以……可以用Eratosthenes筛法预先构造一个
以内的素数表,用来优化试除过程。这个过程的缺点在于
只要稍微大一点的时候,运行时间就是个天文数字了。
Fermat小定理
适用范围:一切正整数
限制:由于伪素数的存在,Fermat小定理本身无法“证明”一个整数是素数
时间复杂度:
算法本身大家也应该很熟悉了:挑一个底数
,去计算
是否成立。在使用快速幂算法的情况下,这个计算过程只需要
次模
的乘法,而每次乘法的开销是
。
现在的问题在于,有的合数
也会满足Fermat小定理(这种n一般叫做“伪素数”),比如说
更有甚者,有一类叫做Carmichael数的合数(最初的几个例子是561,1105,1729,……),对所有的底数
(只要
不是
的因子)都满足Fermat小定理。这就意味着Fermat小定理本身是无法证明
是素数的
尽管如此,如果一个正整数
通过了Fermat小定理的测试,那它是素数的可能性就很大了:
以内有455052511个素数,但是只有14884个以2为底的伪素数,1547个Carmichael数。数学上一般把“通过了Fermat测试的正整数”称为Probable Prime(PRP)。(这个术语好像还没有通行的中文译名)
一般面对一个较大的整数
,在初步的试除过后,要做的下一件事就是用Fermat小定理(或者接下来要介绍的一些加强形式)来做出一个初步的判断;如果
通过了这个测试,说明它有极大的可能性是素数,然后可以用更高端的方法来证明
确实是素数。
Fermat小定理的加强形式,Rabin-Miller测试,以及“工业级别”的素数
适用范围:一切正整数
限制:仍然无法证明一个较大的整数是素数;但是随机取
个底数做测试的话,准确率可以达到至少
。
时间复杂度:
Rabin-Miller测试是基于下面这个事实的:如果
是素数,那么
的情况下,1的平方根只能是
。把这个东西跟Fermat小定理结合起来,就能发现:
如果上面一个式子的右端是1,那么就可以继续开平方根得到
依次类推,直到左边的指数
是奇数不能再开平方根,或者某一步右边变成了-1为止。如果某一步开平方根的时候右端直接从1变成了“不是
的数”,则
是合数。
这个方法虽然也有伪素数的存在(比如
的时候2047可以通过Rabin-Miller测试),但是不会有类似Carmichael数的东西出现了:对于任意的合数
, 总存在一个适当的底数
使得
无法通过以
为底的Rabin-Miller测试。事实上,在
这个范围里,至少有75%的底数满足这个条件。这就意味着,如果我们以随机的方式取出
个底数
,而且正整数
通过了所有的以
为底的Rabin-Miller测试,那么从某种意义上说,
不是素数的概率最多是
。举个例子,如果
,那么这个概率就是
,几乎可以忽略了。所以Rabin-Miller测试是在实际的密码学应用中最常用的判定方法,通过了这个测试的数可以看做是“工业级别”的素数。
额外说一句:很多数学软件里的isprime()或者类似的函数,使用了底分别是2,3,5,7,11,13,17的7次Rabin-Miller测试。这个测试当然不适用于所有的正整数,但是它的最小反例是 341550071728321……所以对于这个值以下的整数来说,“通过这7次Rabin-Miller测试”就可以证明它是素数了。
再额外说一句:如果我们假设广义Riemann猜想(GRH)成立,那么证明
是素数就只需要验证“
可以通过以
为底的Rabin-Miller测试”了。这个算法,如果能严格证明的话,运行时间是
Lucas序列
适用范围:一切正整数
限制:和Fermat小定理一样,无法“证明”一个整数是素数
时间复杂度:也是
大家可能听说过Fibonacci数列满足这么一条性质:如果
是素数,则
,其中
是二次剩余的Jacobi符号。这其实是二次扩域
里面的Fermat小定理的推广:
。
这个结果自然也可以推广到别的二次扩域上:
实际计算的时候就会涉及Fibonacci序列的类似物:一般的有齐次线性二阶递推关系的序列(叫做Lucas Sequence)。这种序列一般的定义是
,
也有通项公式
,其中
。
这时需要验证的同余式就变成了
。
也可以用类似于快速幂的方式进行计算,所以验证这个同余式的时间复杂度也是
。
理所当然,和刚才提到的Fermat小定理一样,这类测试也会有伪素数的存在(Fibonacci数的情况下,最小的例子是323和377),也有类似Rabin-Miller测试的强化版本。
一句题外话:把Rabin-Miller方法和强化的Lucas方法组合起来,我们就得到了当前最强大的Probable Prime判定方法:如果一个正整数
通过了底是2的Rabin-Miller测试,也通过了底是
的强化版Lucas测试(其中
是序列
之中第一个满足
的元素),那
几乎可以确定是素数。这一般叫做Baillie-PSW Test,虽然不严格但是至今为止还没有发现反例!
-----------------------分割线-------------------
以上介绍了一些较快的可以“初步测试”一个正整数是不是素数的办法。如果某个
通过了上面的这些测试,那它肯定有很大的可能性是素数。接下来的问题是:如何去严格的证明
就是素数呢?
接下来有请我们的各种高端方法出场(掌声)。
n-1方法
适用范围:有特殊形式的正整数
对特殊形式的要求:
的素因子大部分已知
时间复杂度:
我们先来回顾一下对Fermat小定理的一个群论证明:如果
是素数,则乘法群
的阶数是
,由群论中的Lagrange定理立得。
这个证明告诉了我们什么?Fermat小定理包含了一些关于群
的信息。如果我们能想办法说明
的阶数就是
,那就足以证明
是素数了。如果我们知道
的所有素因子,那这个验证过程并不困难。
定理(Lucas, 1891):如果存在一个底数
,使得以下两条成立:
对
的每个素因子
,都有
那么
是素数。
证明:这两个条件组合起来可以说明
在
中的阶数就是
,所以
的阶数恰好就是
,从而
是素数。
当然这个算法本身也拥有很大的改进余地。因为对于很多整数
来说我们并不能保证知道
的所有素因子(因子分解一般比素数判定难得多),我们接下来介绍一些“只知道
的一部分因子”的情况下也能生效的办法。
定理(Pocklington, 1914):假设
,其中
的素因子都已知。如果存在一个底数
,使得以下两条成立:
对
的每个素因子
,都有
那么
的每个素因子都是
的形式。特别地,如果
,则
是素数。
这个定理说明了把
分解到“一半”就可以证明
是素数了。后续的一些工作用更加细致的分析把这个“一半”的界限降低到了三分之一 [Brillhart-Lehmer-Selfridge 1975], 30% [Konyagin-Pomerance 1997], 以及
[Coppersmith-Howgrave-Graham, 2008](后两篇文章的算法需要做一些附加的计算,涉及Lattice Reduce以及LLL算法)。 篇幅所限,此处不做进一步展开,有兴趣的读者可以自行查阅文献。
n+1方法
适用范围:有特殊形式的正整数
对特殊形式的要求:
的素因子大部分已知
时间复杂度:
方法和
方法非常类似,只不过涉及的群变成了商群
。如果
是素数且
,那么这个群的阶数就应该是
;反过来,如果我们用跟上一节类似的方法验证了这个群的阶数就是
,那也就说明了
是素数。这个验证的过程用到了之前提到的Lucas序列:如果存在合适的
使得
,且相应的Lucas序列满足
,那么
就是素数。
上一节所说的改动同样生效:如果
,
的素因子都已知且满足
,那么
的每个素因子都是
的形式。同样的,如果这个
足够大(和上一节的结果平行,最差只需要
),那么就可以通过一些附加的计算来证明
是素数。
一句题外话:大家可能听说过检验Mersenne素数
的Lucas-Lehmer测试。这其实是
方法的一个特殊情况:这里
,它的素因子自然只有2,而Lucas-Lehmer测试等价于底是
(或者说
)的时候验证了
。
这两节介绍的
方法是1890-1980年这近一个世纪的时间里仅有的可以证明某个很大的正整数确实是素数的方法。现在所知道的Top-5000的大素数(参考https://primes.utm.edu/primes/home.php)无一例外都是用这两种方法之一证明的,它们的形式也都是“某个素因子都已知的数
顺带一提:如果
都有一些已知的素因子(比如
),那分别验证完两部分条件之后只要
就可以证明了。这一点对于“
的素因子分解并不平凡,但是仍然可以分解出相当一部分”的这种
最有效。典型的例子是Fibonacci素数:
,
,可以由Fibonacci序列的整除性质得到
的大量素因子。
----------------------70-80年代的分割线------------------------
以上的
方法通常被称为“古典的素数判定法”,共同要求是我们需要知道
的一定数量的素因子。对于一般的没有特殊形式的正整数
来说,对
做因子分解是一件很困难的事。以下介绍80年代以后开发出来的不需要
的特殊形式的“现代素数判定法”。
APR-CL算法
适用范围:一切正整数
时间复杂度:
刚才介绍的
方法的本质都在于“确定一个群的大小”:
的时候是
,而
的时候涉及到
的一个二次扩张。