feat(project-euler):添加第7题和第8题的解决方案

📝 docs(project-euler):为第7题添加详细的埃拉托斯特尼筛法文档
This commit is contained in:
2025-12-15 16:30:35 +08:00
parent 7df59c9bcf
commit 0501fef184
3 changed files with 275 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
"""
By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
What is the 10001st prime number?
"""
import time
from math import log
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.6f} seconds")
return result
return wrapper
@timer
def sieve_nth_prime(n: int) -> int | None:
if n == 1:
return 2
# 估算上限第n个质数约在 n*log(n) 附近
limit = int(n * (log(n) + log(log(n)))) + 10 if n > 6 else 20
sieve = [True] * limit
sieve[0:2] = [False, False] # 0和1不是质数
count = 0
for p in range(2, limit):
if sieve[p]:
count += 1
if count == n:
return p
# 标记倍数
sieve[p * p : limit : p] = [False] * ((limit - 1 - p * p) // p + 1)
return None
if __name__ == "__main__":
print(sieve_nth_prime(10001))

View 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))
```

View File

@@ -0,0 +1,79 @@
"""
73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450
Find the thirteen adjacent digits in the 1000-digit number that have the greatest product. What is the value of this product?
"""
import time
from functools import reduce
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.6f} seconds to run.")
return result
return wrapper
@timer
def largest_product_in_series(series: str, n: int) -> int:
if n <= 0 or n > len(series):
return 0
# 转换为整数列表
# 移除所有空格
series = series.replace("\n", "")
series = series.replace(" ", "")
digits = [int(x) for x in series]
max_product = 0
# 遍历所有可能的子序列
for i in range(len(digits) - n + 1):
product = reduce(lambda x, y: x * y, digits[i : i + n])
max_product = max(max_product, product)
return max_product
if __name__ == "__main__":
txt = """73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"""
print(largest_product_in_series(txt, 13))