From 0501fef18474108fad80dc1b1679db7fd5ccdbc8 Mon Sep 17 00:00:00 2001 From: Sidney Zhang Date: Mon, 15 Dec 2025 16:30:35 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(project-euler)=EF=BC=9A?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AC=AC7=E9=A2=98=E5=92=8C=E7=AC=AC8?= =?UTF-8?q?=E9=A2=98=E7=9A=84=E8=A7=A3=E5=86=B3=E6=96=B9=E6=A1=88=20?= =?UTF-8?q?=F0=9F=93=9D=20docs(project-euler)=EF=BC=9A=E4=B8=BA=E7=AC=AC7?= =?UTF-8?q?=E9=A2=98=E6=B7=BB=E5=8A=A0=E8=AF=A6=E7=BB=86=E7=9A=84=E5=9F=83?= =?UTF-8?q?=E6=8B=89=E6=89=98=E6=96=AF=E7=89=B9=E5=B0=BC=E7=AD=9B=E6=B3=95?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solutions/0007.10001stPrime/euler_7.py | 43 +++++++ solutions/0007.10001stPrime/readme.md | 153 +++++++++++++++++++++++ solutions/0008.LargestProduct/eular_8.py | 79 ++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 solutions/0007.10001stPrime/euler_7.py create mode 100644 solutions/0007.10001stPrime/readme.md create mode 100644 solutions/0008.LargestProduct/eular_8.py diff --git a/solutions/0007.10001stPrime/euler_7.py b/solutions/0007.10001stPrime/euler_7.py new file mode 100644 index 0000000..ba210a0 --- /dev/null +++ b/solutions/0007.10001stPrime/euler_7.py @@ -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)) diff --git a/solutions/0007.10001stPrime/readme.md b/solutions/0007.10001stPrime/readme.md new file mode 100644 index 0000000..d518707 --- /dev/null +++ b/solutions/0007.10001stPrime/readme.md @@ -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)) +``` diff --git a/solutions/0008.LargestProduct/eular_8.py b/solutions/0008.LargestProduct/eular_8.py new file mode 100644 index 0000000..9530e4e --- /dev/null +++ b/solutions/0008.LargestProduct/eular_8.py @@ -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))