厄拉多塞筛法
算法介绍
厄拉多塞筛算法(Eratosthenes Sieve)是一种求素数的方法,由古希腊数学家厄拉多塞提出。它的原理是,给定一个数 n,从 2 开始依次将 \(\sqrt{n}\) 以内的素数的倍数标记为合数,标记完成后,剩余未被标记的数为素数(从 2 开始)。如此可省去检查每个数的步骤,使筛选素数的过程更加简单。厄拉多塞筛算法具体步骤如下:
- 读取输入的数 n,将 2 到 n 的所有整数记录在表中
- 从 2 开始,划去表中所有 2 的倍数
- 由小到大寻找表中下一个未被划去的整数,再划去表中所有该整数的倍数
- 重复第(3)步,直到找到的整数大于 \(\sqrt{n}\) 为止
- 表中所有未被划去的整数均为素数
算法流程图
具体代码(python)
def sieve_prime(n: int) -> list:
"""厄拉托塞筛法"""
if n < 2: # 不存在小于 2 的素数
return []
is_prime = [True for _ in range(n + 1)]
# 遍历 i=2 到 根号 n
for i in range(2, int(pow(n, 0.5)) + 1):
if is_prime[i]: # 筛去 i 的倍数
for j in range(i * i, n + 1, i):
is_prime[j] = False
# 返回 2 到 n 中未被筛去的数
return [i for i in range(2, n + 1) if is_prime[i]]
在筛去 i 的倍数的时候,第一个数是 \(i \times i\) 而不是 i,这是因为对于所有 \(k \times i , \; k < i\),都在前面被筛过,故可以跳过这些数
测试样例和结果
- 102 以内的素数共 25 个,最后两个为 89, 97
- 103 以内的素数共 168 个,最后两个为 991, 997
- 104 以内的素数共 1229 个,最后两个为 9967, 9973
- 105 以内的素数共 9592 个,最后两个为 99989, 99991
- 106 以内的素数共 78498 个,最后两个为 999979, 999983
- 107 以内的素数共 664579 个,最后两个为 9999973, 9999991
一些改进
这个算法很大的一个问题是对空间很不友好,需要存储很大的一个素数表
除了 2 之外,所有的素数都是奇数,那么在筛的时候只需要考虑奇数即可
在构建表格的时候,先前将下标 i 对应整数 i,现在可以将下标 i 对应整数 2i+3,节约一半的存储空间
def sieve_prime(n: int) -> list:
"""厄拉托塞筛法"""
if n < 2: # 不存在小于 2 的素数
return []
elif n == 2:
return [2]
end = (n - 3) // 2
is_prime = [True for _ in range(end + 1)]
# 遍历 i=3 到 根号 n
for i in range(3, int(pow(n, 0.5)) + 1, 2):
k = (i - 3) // 2 # 将 i 映射回下标 k
if is_prime[k]: # 筛去 i 的倍数
for j in range(i * i, n + 1, 2 * i):
is_prime[(j - 3) // 2] = False
# 返回 2 到 n 中未被筛去的数
return [2] + [2 * i + 3 for i in range(end + 1) if is_prime[i]]
最后
除了厄拉多塞筛法之外,还有欧拉筛等筛法
使用 python 时间和空间效率都较低,对于标记素数,可以采用 c++ 的 bitset,bitset 是以比特为单位标记的,会极大降低存储消耗
参考资料:《密码学实验教程》