Files
SolutionEuler/solutions/0000_0029/0007.10001stPrime/readme.md
2025-12-26 17:35:14 +08:00

154 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 埃拉托斯特尼筛法
埃拉托斯特尼筛法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))
```