小知识,大挑战!本文正在参与「程序员必备小知识」创作活动。本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
大家好,我是披着狼皮的🐏。今天给大家介绍几个用于素性测试(primality test)的确定性算法,适用于判断给定整数 n
是否为质数。注意,这里不考虑 0
、1
等其他不是质数也不是合数的数,在实际应用上大家记得自己加 if
判断。
试除法(Trial Division)+ 优化
最直接的方法,遍历从 2 到 n-1
的所有数字,并检查其是否能整除 n
,找到了便返回 False
。
def is_prime(n):
for x in range(2, n):
if n % x == 0:
return False
return True
复制代码
优化 1
如果 n
是合数,那么必然存在两个约数
和
,且
。简单来说,因数是一对一对出现的,而
n-1
的数都试一次,到 x == sqrt(n)
就可以停下了。
from math import isqrt // Python 版本 >= 3.8
def is_prime(n):
for x in range(2, isqrt(n) + 1):
if n % x == 0:
return False
return True
复制代码
优化 2
如果 n
能被一个偶数整除,那么它一定能被 2 整除,合理吧?因此,我们可以跳过除了 2 以外的所有偶数。
from math import isqrt // Python 版本 >= 3.8
def is_prime(n):
if n == 2:
return True
elif n % 2 == 0:
return False
for x in range(3, isqrt(n) + 1, 2):
if n % x == 0:
return False
return True
复制代码
优化 3
除了
2
与3
,所有质数一定能表达成6k+1
或6k+5
的形式,k
为一个正整数。(质数一定能表达成这个形式,但能表达成这个形式的数不一定是质数。)
论证
6k
能被6
整除,不是质数;6k+2
和6k+4
能被2
整除,不是质数;6k+3
能被3
整除,不是质数;
那么就只剩下 6k+1
和 6k+5
有可能质数了。
注:6k+5
也可以写成 6k-1
,因为
。
对于有可能是质数的数,我们还得用试除法,范围还能再进一步的缩小 —— 在能表示成 6k+1
或 6k+5
的数中寻找因数就可以了。
为什么?
所有的合数都能分解成质数,所以 n
只需要与可能是质数的数(即 6k+1
或 6k+5
)判断就可以了。
def is_prime3(n):
if n == 2 or n == 3:
return True
# 不能表达成 6k+1 或 6k+5 的数一定不是质数
if n % 6 != 1 and n % 6 != 5:
return False
# 进一步的判断
for x in range(5, int(sqrt(n)) + 1, 6):
if n % x == 0 or n % (x + 2) == 0:
return False
return True
复制代码
威尔逊定理(Wilson's Theorem)
数论四大定理之一:
当且仅当
是质数时:
简单来说,如果 n
是质数,则 n-1
的阶乘加 1 除以 n
的余数为 0。
论证
里的每个元素
都会有一个模
的倒数
,即
。(这里就不论证这句话了……)
若
,
只有 1 和
的模倒数是自身,
内的每个数都有一个独特的模倒数。两两配对,那么我们就有几个模
等于
的对。
证明完毕,上代码。
from math import factorial
def is_prime(n):
return (factorial(n - 1) + 1) % n == 0
复制代码
AKS素性测试(Agrawal–Kayal–Saxena Primality Test)
是质数当且仅当:
能整除
的展开式的所有系数。
论证
利用二项式定理(binomial theorem),展开原式:
可以看得出,
这项去掉了
展开式的首尾项。那么上方的定理也可以这么表达:
是质数当且仅当:
能整除
。
让
,
显然
能整除
,那么
就能整除
。如果一个数能整除一个几个数的积,那么它一定至少能整除它们之一。由于
不能整除
,因为 且 是质数;
,因为 且 是质数;
那么它一定能整除
。
这个定理对于质数成立。
当
为合数时,
让
为
的因数,
。显而易见,
。
如果上方的式子可以被
整除,那么必然会有一个正整数
使得
。
让我们看看
是不是整数。
根据威尔逊定理,
不可能是整数。这个定理对于合数不成立。
论证完毕,代码如下:
from math import comb // Python 版本 >= 3.8
def is_prime(n):
for r in range(1, n): // (n-1) + 1 = n
if comb(n, r) % n != 0:
return False
return True
复制代码
优化
,
range
的范围可以缩小一半。
from math import comb // Python 版本 >= 3.8
def is_prime(n):
for r in range(1, n // 2 + 1):
if comb(n, r) % n != 0:
return False
return True
复制代码
赶紧点赞,不然你下次面试遇到的题目就是这题。
下篇文章将给大家介绍几个效率更高的概率性算法(probabilistic test)。就这样,拜拜。
近期评论