质数的费马检查
SICP 1.2.6 介绍了一种对数复杂度的素数检查方法。
问题背景
质数又称素数。对于大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫质数;否则称合数。1既不是质数也不是合数。
如何编程检查一个数是不是质数?我所知道的方法有:
- 因数排查
- 素数筛
- 费马检查
其中,费马检查是一种概率算法,有一定的错误率。
数学原理与编程实现
费马小定理
- 如果 n n n是质数, a a a是小于 n n n的任意正整数,那么 a n − 1 ≡ 1 ( m o d n ) a^{n-1}\equiv 1(mod\ n) an−1≡1(mod n).
费马小定理描述了质数的一个性质,满足此性质的却不都是质数。
此定理指导我们编程如下,
def fermat_test(n):
import random
a = random.randint(1, n - 1)
return a**(n - 1) % n == 1
然而,Carmichael数很可能骗过费马检查。Carmichael数不是质数,但是很可能通过费马检查。
使用下列实验代码:
Carmichaels = 561, 1105, 1729, 2465, 2821, 6601
normal_numbers = 563, 1107, 1727, 3465, 4821, 6607
ns = Carmichaels
for n in ns:
t = [i**(n - 1) % n == 1 for i in range(1, n)]
print('%-5d%8.2f' % (n, sum(t) / len(t)))
上面这段代码计算Carmichael数通过费马检查的概率,结果如下:
Carmichael | 通过费马检查的概率 |
---|---|
561 | 0.57 |
1105 | 0.70 |
1729 | 0.75 |
2465 | 0.73 |
2821 | 0.77 |
6601 | 0.80 |
通过率都高于50%!与此相对,一般合数的费马通过率都接近于0.
二次探测定理
- 如果 n n n是质数,则 x 2 ≡ 1 ( m o d n ) x^2\equiv1(mod\ n) x2≡1(mod n)的解为 x = 1 x=1 x=1或 x = n − 1 x=n-1 x=n−1.
只要在检验 a n − 1 ≡ 1 ( m o d n ) a^{n-1}\equiv 1(mod\ n) an−1≡1(mod n),即计算 a n − 1 m o d n a^{n-1}mod\ n an−1mod n时,检查中间平方步骤的得数。如果遇到“ 1 1 1取模 n n n的非平凡平方根”,即 1 1 1与 n − 1 n-1 n−1之外的 x x x满足 x 2 ≡ 1 ( m o d n ) x^2\equiv1(mod\ n) x2≡1(mod n),那么 n n n就不是质数。
def miller_rabin_test(n):
def non_trival_sqrt(n, m):
if n == 1 or n == m - 1:
return False
return n**2 % m == 1
def expmod(base, exp, m):
if exp == 0:
return 1
elif exp % 2 == 0:
x = expmod(base, exp // 2, m)
if non_trival_sqrt(x, m):
return 0
else:
return x**2 % m
else:
return base * expmod(base, exp - 1, m) % m
a = random.randint(1, n - 1)
return expmod(a, n - 1, n) == 1
这就是Miller-Rabin检查。经实验,Miller-Rabin检查能大大降低Carmichael数的通过率。
Carmichael | Miller-Rabin通过率 |
---|---|
561 | 0.02 |
1105 | 0.03 |
1729 | 0.09 |
2465 | 0.03 |
2821 | 0.10 |
6601 | 0.05 |
参考资料
- SICP
- 非递归实现