# 埃拉托斯特尼筛法 埃拉托斯特尼筛法(Sieve of Eratosthenes)是最古老、最优雅的质数筛选算法, 由古希腊数学家埃拉托斯特尼在公元前3世纪提出。 ### **核心思想** **" multiples of primes are composite "** (质数的倍数都是合数) 从2开始,逐个标记每个质数的所有倍数为非质数,剩下的未标记数就是质数。 --- ### **算法步骤** **示例:找出 ≤30 的所有质数** 1. **初始化**:列出 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 ``` 2. **筛选过程**: - **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,停止 3. **结果**:剩下的未标记数 ``` 2 3 5 7 11 13 17 19 23 29 ``` --- ### **关键优化点** **只需筛到 √n**: 要找出 ≤n 的所有质数,只需检查到 √n 即可。 **证明**:任何合数 m ≤ n 必有一个质因数 ≤ √n,否则 m = p₁×p₂ > √n×√n = n,矛盾。 --- ### **正确实现(Python)** ```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 --- ### **进阶优化技巧** 1. **仅筛奇数**:偶数除了2都不是质数,内存减半 ```python sieve = [True] * ((n + 1) // 2) # 索引i对应数字 2*i+1 ``` 2. **位压缩**:用bit array而非bool,内存再降8倍 ```python from bitarray import bitarray sieve = bitarray(n + 1) sieve.setall(True) ``` 3. **分段筛选**:当n极大(>10⁸)时,分段加载到缓存 --- ### **适用场景** ✅ **适合**: - 需要一次性生成大量质数 - 频繁查询范围内的质数 - n在百万到千万级别 ❌ **不适合**: - 只需判断单个数是否为质数 - n极大(>10¹⁰)且内存有限 - 仅需要第n个质数而非全部 --- 分段筛法:当n极大(>10⁸)时,分段加载到缓存,减少内存占用。 ```python 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 库 ```python # 生产环境推荐使用 pyprimesieve 库 # pip install pyprimesieve from pyprimesieve import nth_prime print(nth_prime(10**7)) ```