本文内容主要参考《程序员的数学思维修炼》一书中对素数和余数的讲解及这篇博文:
Miller-Rabin素数测试学习笔记
以及这篇文章:
数论部分第一节:素数与素性测试(这篇文章非常好,很清晰的讲解了从素数定义到素数检测算法的过程,比我看到的其他所有资料都好,就是有点长)
先介绍一个概念:互质(或叫互素)
如果a和b两个数的公约数只有1,那么a和b互质,记做(c,m)=1。
根据质数定义,质数与所有小于它的正整数互质。
再介绍几个余数的性质及推广出的几个变换:
(a + b) mod p == (a mod p + b mod p) mod p
(a * b) mod p == ((a mod p) * (b mod p)) mod p
ab mod p == (a mod p)b
要想理解Miller-Rabin,需要先了解一下费马小定理。
Miller-Rabin质数检测算法是基于费马小定理的改进概率算法。
假如p是质数,且(a,p)=1 [公约数为1,互质],则 a(p-1)≡1(mod p)。[或者写成( ap-1 mod p ≡ 1)的形式,mod是取模的意思,下同]
解释:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。默认a小于p。
(摘自百度百科)
费马小定理是关于质数判定的必要非充分条件,意思是说p是质数且a、p互质的情况下a(p-1)≡1(mod p)是对的,但满足恒等式且a、p互质,p不一定是质数。由此引出费马伪素数(以下简称伪素数)的概念:
伪素数,又叫做伪质数:它满足费马小定理,但其本身却不是质数。若n能整除(2n-1-1),并且n是非偶数的合数,那么n就是伪质数。
(摘自百度百科)
事实上,偶伪质数也是存在的,并且已被证明有无穷多个。
总结一下上面几句话的意思:
所有的质数一定能满足费马小定理,满足不了的肯定不是质数。但是满足费马小定理的不一定全是质数,有些合数也满足费马小定理,叫做伪质数(伪素数)。
例如,众所周知,偶数一定不是质数,但是某些偶数也可以满足费马小定理,这些数叫做偶伪质数。
*除了费马伪质数以外,还有欧拉伪素数、欧拉-雅可比伪素数等
在满足费马小定理的合数(伪质数)里,有一种很特殊的数,叫做卡迈克尔数(Carmichael数),其定义为:
对于合数n,如果对于所有正整数b,b和n互素,都有同余式b^(n-1)≡ 1 (mod n) [或(bn-1 mod n ≡ 1)]成立,则合数n为Carmichael数。
(摘自百度百科)
卡迈克尔数的定义和费马小定理的定义很相似,他们之间的区别在于下面两点:
第二点区别在于,费马小定理在判断是否是质数(或者伪质数)时,只需要任选一个整数进行计算,而判断卡迈克尔数则需要把所有小于n的数都带入费马小定理中计算一遍,需要经过多轮的筛选。卡迈克尔数的要求在费马小定理的基础上更加的严格。
综上可见,卡迈克尔数一定是合数,而且一定满足费马小定理。卡迈克尔数虽然条件苛刻,但也被证明有无穷多个,前一亿个
自然数中,有255个卡迈克尔数。
庆幸的是,虽然费马小定理的逆定理不完全成立,但在绝大多数情况下还是有效的。
基于费马小定理 ap-1 mod p≡1这个公式,随机选取若干个小于p的正整数a,若所有的a都满足p-1 mod p≡1,则基本认为p是质数。
Fermat素性测试只能大概率排除伪质数,而不能排除所有的伪质数,就是因为有卡迈克尔数的存在。从定义中也能看出这点。
Fermat素性测试实际上就是对费马小定理的应用。
统计表明,在前10亿个自然数中共有50847534个满足费马小定理的数,其中满足2(n-1) mod n = 1的合数n有5597个,这些合数都是以2为底的伪质数。有些伪质数可能通过了以2为底的测试,却不能通过以3为底的测试(3(n-1) mod n = 1)。能同时通过2和3测试的合数叫做以2和3为底的伪质数,前10亿中只有1272个。
所以,Fermat素性测试需要做相互独立的若干轮随机测试来降低出错的可能性。这个逻辑类似布隆过滤器。
而取遍1到n-1的所有整数进行素性测试,仍然没有被查出来的伪质数就被称为卡迈克尔数,前10亿中有600多个。
Fermat素性测试之所以不那么靠谱就是因为有卡迈克尔数的存在,这可以说是费马小定理的盲点吧。
Miller-Rabin素数测试基于下面的新定理:
如果p是素数,x是小于p的正整数,且x2 mod p = 1,那么要么x=1,要么x=p-1。这是显然的,因为x2 mod p = 1相当于p能整除x2-1,也即p能整除(x+1)(x-1)。由于p是素数,那么只可能是x-1能被p整除(此时x=1)或x+1能被p整除(此时x=p-1)。(ps:素数p只能整除0和它自己)
(摘自数论部分第一节:素数与素性测试)
通过上面这个定理来加强费马小定理,可以得到这个公式:
ad*2r mod p =1
根据上面的定理和公式,在进行Fermat测试时,还需测试d*2r-1时ad*2r mod p是否等于1或者p-1,如果通过则测试d*2r-2时是否成立,直到r=0或者ad*2r-s mod p = p-1,其中0≤s≤r-1。
要测试N是否为素数,首先将N-1分解为 2sd。在每次测试开始时,先随机选一个介于[1,N-1]的整数a,之后如果对所有的r∈[0,s-1],若ad mod N≠1且a2rd≠-1则N是合数。否则,N有3/4的概率为素数。
(摘自维基百科)
上面用新定理加强费马小定理的原理我本人并不是很理解,所以这里用一种自己想到的方法来解释(可能是错的,看看就好 ʅ(‾◡◝))。
ap-1 mod p=1 <== 原公式
ad*2r mod p=1 <== 将p-1改写成d*2r的形式后
(ad*2r-1)2 mod p=1 <== 从指数d*2r中拆一个2出来放在外面后
(ad*2r-1 mod p)*(ad*2r-1 mod p)=1 <== 利用本文开头介绍的余数定理改写后
两个相同的数相乘等于1,所以(ad*2r-1 mod p)的结果只能是±1,其中-1等价于p-1。
如果最上面的式子成立,那么后面的式子都应该成立,并且可以重复其中的过程直到r=1或者等式右边是p-1。如果右边是p-1,那么式子就不能继续分解下去,过程到此为止。
有人将上面利用加强费马小定理测试素性的过程叫做Miller测试。
通过上面的素性测试可以有效筛掉大部分伪素数。比如,基于原本的费马小定理,最小的伪素数是341,最小的卡迈克尔数是561;而用加强费马小定理,最小的伪素数是2047,最小的底为2和3的伪素数则是1 373 653,可见效果是非常显著的。
完整的Miller-Rabin素数检测过程就是:进行相互独立的k轮测试,每轮在小于待测素数p的正整数中随机选取a的值,利用加强费马小定理进行Miller测试,通过所有测试的p就可以认为是素数,p被误判的概率是4-k。
下面是Miller-Rabin素数检测的流程图:
变量s是控制测试轮数的
有一些非常实用的现成结论可以在进行素数测试时降低算法运行时间:如果被测数小于4 759 123 141(47亿),那么只需要测试三个底数2, 7和61就足够了;如果每次都用前7个素数(2, 3, 5, 7, 11, 13和17)进行测试,所有不超过341 550 071 728 320(3百万亿)的数都是正确的;如果选用2, 3, 7, 61和24251作为底数,那么10^16内唯一的强伪素数为46 856 248 255 981(4.6*1013)。
所以其实没有必要将流程图中的0~s之间所有的数都测一遍,就像上面所说,选择几个特定的数就足以覆盖大部分情况。