feat(project):添加欧拉项目第4、5题解决方案及文档

📝 docs(README):更新项目描述并添加核心数学理念说明
🔧 chore(pyproject.toml):更新项目描述信息
♻️ refactor(euler_3.py):改进质因数分解函数并添加类型注解
💡 docs(readme):添加第4题数学分析文档和算法说明
 test(euler_3.py):添加主函数测试用例验证质因数分解功能
This commit is contained in:
2025-12-15 12:12:03 +08:00
parent d1af6aa880
commit 65999c8456
7 changed files with 203 additions and 2 deletions

View File

@@ -4,3 +4,11 @@
也会有一些新奇的想法。主要使用 Python 解决,也会有 Haskell 、 Rust 或者 Lean4 的解决方法。 也会有一些新奇的想法。主要使用 Python 解决,也会有 Haskell 、 Rust 或者 Lean4 的解决方法。
项目使用 `uv` 进行管理,毕竟主要还是使用 Python来写的。 项目使用 `uv` 进行管理,毕竟主要还是使用 Python来写的。
-----
**KEY POINT **
要把数学回归数学,而不是回归程序。
从数学角度去考虑问题,用数学化简计算逻辑。

View File

@@ -1,7 +1,7 @@
[project] [project]
name = "projecteuler" name = "projecteuler"
version = "0.1.0" version = "0.1.0"
description = "Add your description here" description = "euler 项目的解题。主要为python。"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []

View File

@@ -1,3 +1,8 @@
"""
The prime factors of 13195 are 5, 7, 13and 29.
What is the largest prime factor of the number 600851475143 ?
"""
import random import random
from math import gcd from math import gcd
from typing import List, Set from typing import List, Set
@@ -34,7 +39,7 @@ def is_probable_prime(n: int, trials: int = 10) -> bool:
return True return True
def pollards_rho(n: int, max_iter: int = 100000) -> int: def pollards_rho(n: int, max_iter: int = 100000) -> int | None:
""" """
Pollard's Rho 算法返回n的一个非平凡因子 Pollard's Rho 算法返回n的一个非平凡因子
@@ -46,6 +51,7 @@ def pollards_rho(n: int, max_iter: int = 100000) -> int:
n的一个因子可能是质数也可能是合数 n的一个因子可能是质数也可能是合数
若失败返回None 若失败返回None
""" """
if n % 2 == 0: if n % 2 == 0:
return 2 return 2
@@ -86,6 +92,8 @@ def factorize(n: int | None) -> List[int | None]:
""" """
if n == 1: if n == 1:
return [] return []
if n is None:
return [None]
# 如果是质数,直接返回 # 如果是质数,直接返回
if is_probable_prime(n): if is_probable_prime(n):
@@ -94,6 +102,9 @@ def factorize(n: int | None) -> List[int | None]:
# 获取一个因子 # 获取一个因子
factor = pollards_rho(n) factor = pollards_rho(n)
if factor is None:
return [None]
# 递归分解 # 递归分解
return factorize(factor) + factorize(n // factor) return factorize(factor) + factorize(n // factor)
@@ -101,3 +112,7 @@ def factorize(n: int | None) -> List[int | None]:
def get_prime_factors(n: int) -> Set[int | None]: def get_prime_factors(n: int) -> Set[int | None]:
"""获取所有不重复的质因数""" """获取所有不重复的质因数"""
return set(factorize(n)) return set(factorize(n))
if __name__ == "__main__":
print(get_prime_factors(60)) # {2, 3, 5}

View File

@@ -0,0 +1,52 @@
"""
A palindromic number reads the same both ways.
The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
"""
def is_palindrome(n: int) -> bool:
return str(n) == str(n)[::-1]
def initial_idea() -> None:
a = 999
b = 999
y = (0, 0)
max_palindrome = 0
for i in range(a, 99, -1):
for j in range(b, 99, -1):
product = i * j
if is_palindrome(product) and product > max_palindrome:
max_palindrome = product
y = (i, j)
print(f"{max_palindrome} = {y[0]} × {y[1]}")
def largest_palindrome_product():
max_palindrome = 0
max_factors = (0, 0)
# 外层循环从大到小且只遍历11的倍数
for i in range(990, 100, -11): # 从990开始最大的11的倍数
# 内层循环从i开始避免重复利用乘法交换律
for j in range(999, i - 1, -1):
product = i * j
# 提前终止:如果乘积已小于当前最大值
if product <= max_palindrome:
break
# 检查是否为回文数
if str(product) == str(product)[::-1]:
max_palindrome = product
max_factors = (i, j)
break # 找到即可跳出内层循环
return max_palindrome, max_factors
if __name__ == "__main__":
initial_idea()
x, y = largest_palindrome_product()
print(f"{x} = {y[0]} × {y[1]}")

View File

@@ -0,0 +1,48 @@
从数学角度,**快速**找到两个三位数相乘得到的最大回文数。
## 核心数学洞察
首先,两个三位数最大的乘积是: 999 × 999 = 998001 。所以最大的回文数一定是6位的。
**1. 回文数的结构性质**
一个6位回文数可以表示为
$$
\overline{abccba} = 100000a + 10000b + 1000c + 100c + 10b + a = 100001a + 10010b + 1100c = 11 \times (9091a + 910b + 100c)
$$
**关键结论**所有6位回文数都是**11的倍数**。
**2. 质因数推论**
如果乘积 $p \times q$ 是回文数且这个回文数是11的倍数那么
- 由于11是质数**p和q中至少有一个是11的倍数**
- 这样搜索空间直接缩小为原来的1/11
## 最优算法策略
```python
def largest_palindrome_product():
max_palindrome = 0
max_factors = (0, 0)
# 外层循环从大到小且只遍历11的倍数
for i in range(990, 100, -11): # 从990开始最大的11的倍数
# 内层循环从i开始避免重复利用乘法交换律
for j in range(999, i-1, -1):
product = i * j
# 提前终止:如果乘积已小于当前最大值
if product <= max_palindrome:
break
# 检查是否为回文数
if str(product) == str(product)[::-1]:
max_palindrome = product
max_factors = (i, j)
break # 找到即可跳出内层循环
return max_palindrome, max_factors
# 结果906609 = 913 × 993
```

View File

@@ -0,0 +1,67 @@
"""
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
"""
import math
import random
def is_probable_prime(n: int, trials: int = 10) -> bool:
"""Miller-Rabin素性测试快速判断是否为质数"""
if n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0:
return False
# 将 n-1 写成 d * 2^s 的形式
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
# 测试
for _ in range(trials):
a = random.randrange(2, n - 1)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(s - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
def primes_up_to(n: int) -> list[int]:
if n < 2:
return []
if n == 2:
return [2]
if n == 3:
return [2, 3]
primes = [2, 3]
for i in range(5, n + 1, 2):
if is_probable_prime(i):
primes.append(i)
return primes
def smallest_multiple(n: int) -> int:
result = 1
for p in primes_up_to(n):
# 找出最大幂次
exp = int(math.log(n, p))
result *= p**exp
return result
if __name__ == "__main__":
print(smallest_multiple(20))

View File

@@ -0,0 +1,11 @@
想法来自: [A003418](https://oeis.org/A003418) 。
对于 [a .. b] 这个连续整数区间,最小的可整除的整数,就是:
**小于b的所有质数分别小于b的最大幂值的乘积**
计算步骤就是:
1. 找出不超过 $b$ 的所有质数 ${p}$
2. 对每个质数 $p$ ,找出最大指数 $e$ 使得 $p^e \leq b$
3. 将所有 $p^e$ 相乘