埃拉托斯特尼筛法
埃拉托斯特尼筛法(Sieve of Eratosthenes)是最古老、最优雅的质数筛选算法, 由古希腊数学家埃拉托斯特尼在公元前3世纪提出。
核心思想
" multiples of primes are composite "
(质数的倍数都是合数)
从2开始,逐个标记每个质数的所有倍数为非质数,剩下的未标记数就是质数。
算法步骤
示例:找出 ≤30 的所有质数
-
初始化:列出 2 到 30 的所有整数
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 -
筛选过程:
- p=2:保留2,标记所有2的倍数(4,6,8...)
- p=3:下一个未标记的是3,保留3,标记3的倍数(6,9,12...)
- p=5:下一个未标记的是5,保留5,标记5的倍数(10,15,20,25,30)
- p=7:7²=49 > 30,停止
-
结果:剩下的未标记数
2 3 5 7 11 13 17 19 23 29
关键优化点
只需筛到 √n:
要找出 ≤n 的所有质数,只需检查到 √n 即可。
证明:任何合数 m ≤ n 必有一个质因数 ≤ √n,否则 m = p₁×p₂ > √n×√n = n,矛盾。
正确实现(Python)
def sieve(n):
"""返回所有小于等于n的质数"""
if n < 2:
return []
sieve = [True] * (n + 1)
sieve[0:2] = [False, False] # 0和1不是质数
for p in range(2, int(n**0.5) + 1):
if sieve[p]:
# 从p²开始标记,更小的倍数已被前面的质数标记过
sieve[p*p:n+1:p] = [False] * ((n - p*p) // p + 1)
return [i for i, is_prime in enumerate(sieve) if is_prime]
复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间 | O(n log log n) | 近乎线性,非常高效 |
| 空间 | O(n) | 需要布尔数组存储每个数 |
推导:
质数p会标记 n/p 个数,总操作量 ≈ n×(1/2 + 1/3 + 1/5 + ... + 1/p_k)
根据质数定理,该和式 ≈ n log log n
进阶优化技巧
-
仅筛奇数:偶数除了2都不是质数,内存减半
sieve = [True] * ((n + 1) // 2) # 索引i对应数字 2*i+1 -
位压缩:用bit array而非bool,内存再降8倍
from bitarray import bitarray sieve = bitarray(n + 1) sieve.setall(True) -
分段筛选:当n极大(>10⁸)时,分段加载到缓存
适用场景
✅ 适合:
- 需要一次性生成大量质数
- 频繁查询范围内的质数
- n在百万到千万级别
❌ 不适合:
- 只需判断单个数是否为质数
- n极大(>10¹⁰)且内存有限
- 仅需要第n个质数而非全部
分段筛法:当n极大(>10⁸)时,分段加载到缓存,减少内存占用。
def segmented_nth_prime(n, segment_size=100000):
# 先用小筛法生成基础质数
base_limit = int((n * (log(n) + log(log(n))))**0.5) + 1
base_primes = sieve_primes(base_limit)
count = len(base_primes)
if count >= n:
return base_primes[n-1]
low = base_limit
while True:
high = low + segment_size
segment = [True] * segment_size
for p in base_primes:
start = ((low + p - 1) // p) * p
if start < p * p:
start = p * p
for multiple in range(start, high, p):
segment[multiple - low] = False
for i, is_p in enumerate(segment):
if is_p and i + low > 1:
count += 1
if count == n:
return i + low
low = high
生产环境推荐使用 pyprimesieve 库
# 生产环境推荐使用 pyprimesieve 库
# pip install pyprimesieve
from pyprimesieve import nth_prime
print(nth_prime(10**7))