Symbol(clack:cancel)
This commit is contained in:
153
solutions/0000_0029/0007.10001stPrime/readme.md
Normal file
153
solutions/0000_0029/0007.10001stPrime/readme.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# 埃拉托斯特尼筛法
|
||||
|
||||
埃拉托斯特尼筛法(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))
|
||||
```
|
||||
Reference in New Issue
Block a user