✨ 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:
@@ -4,3 +4,11 @@
|
||||
也会有一些新奇的想法。主要使用 Python 解决,也会有 Haskell 、 Rust 或者 Lean4 的解决方法。
|
||||
|
||||
项目使用 `uv` 进行管理,毕竟主要还是使用 Python来写的。
|
||||
|
||||
|
||||
-----
|
||||
|
||||
**KEY POINT :**
|
||||
|
||||
要把数学回归数学,而不是回归程序。
|
||||
从数学角度去考虑问题,用数学化简计算逻辑。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "projecteuler"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
description = "euler 项目的解题。主要为python。"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
@@ -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
|
||||
from math import gcd
|
||||
from typing import List, Set
|
||||
@@ -34,7 +39,7 @@ def is_probable_prime(n: int, trials: int = 10) -> bool:
|
||||
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的一个非平凡因子
|
||||
|
||||
@@ -46,6 +51,7 @@ def pollards_rho(n: int, max_iter: int = 100000) -> int:
|
||||
n的一个因子(可能是质数也可能是合数)
|
||||
若失败返回None
|
||||
"""
|
||||
|
||||
if n % 2 == 0:
|
||||
return 2
|
||||
|
||||
@@ -86,6 +92,8 @@ def factorize(n: int | None) -> List[int | None]:
|
||||
"""
|
||||
if n == 1:
|
||||
return []
|
||||
if n is None:
|
||||
return [None]
|
||||
|
||||
# 如果是质数,直接返回
|
||||
if is_probable_prime(n):
|
||||
@@ -94,6 +102,9 @@ def factorize(n: int | None) -> List[int | None]:
|
||||
# 获取一个因子
|
||||
factor = pollards_rho(n)
|
||||
|
||||
if factor is None:
|
||||
return [None]
|
||||
|
||||
# 递归分解
|
||||
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]:
|
||||
"""获取所有不重复的质因数"""
|
||||
return set(factorize(n))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(get_prime_factors(60)) # {2, 3, 5}
|
||||
|
||||
52
solutions/0004.palindrome/eular_4.py
Normal file
52
solutions/0004.palindrome/eular_4.py
Normal 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]}")
|
||||
48
solutions/0004.palindrome/readme.md
Normal file
48
solutions/0004.palindrome/readme.md
Normal 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
|
||||
```
|
||||
67
solutions/0005.smallestMultiple/eular_5.py
Normal file
67
solutions/0005.smallestMultiple/eular_5.py
Normal 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))
|
||||
11
solutions/0005.smallestMultiple/readme.md
Normal file
11
solutions/0005.smallestMultiple/readme.md
Normal 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$ 相乘
|
||||
Reference in New Issue
Block a user