From 65999c8456e1dd15f059ef40c1af192e954b9bfc Mon Sep 17 00:00:00 2001 From: Sidney Zhang Date: Mon, 15 Dec 2025 12:12:03 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(project)=EF=BC=9A=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=AC=A7=E6=8B=89=E9=A1=B9=E7=9B=AE=E7=AC=AC4?= =?UTF-8?q?=E3=80=815=E9=A2=98=E8=A7=A3=E5=86=B3=E6=96=B9=E6=A1=88?= =?UTF-8?q?=E5=8F=8A=E6=96=87=E6=A1=A3=20=F0=9F=93=9D=20docs(README)?= =?UTF-8?q?=EF=BC=9A=E6=9B=B4=E6=96=B0=E9=A1=B9=E7=9B=AE=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E6=A0=B8=E5=BF=83=E6=95=B0=E5=AD=A6?= =?UTF-8?q?=E7=90=86=E5=BF=B5=E8=AF=B4=E6=98=8E=20=F0=9F=94=A7=20chore(pyp?= =?UTF-8?q?roject.toml)=EF=BC=9A=E6=9B=B4=E6=96=B0=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=BF=A1=E6=81=AF=20=E2=99=BB=EF=B8=8F=20ref?= =?UTF-8?q?actor(euler=5F3.py)=EF=BC=9A=E6=94=B9=E8=BF=9B=E8=B4=A8?= =?UTF-8?q?=E5=9B=A0=E6=95=B0=E5=88=86=E8=A7=A3=E5=87=BD=E6=95=B0=E5=B9=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=B1=BB=E5=9E=8B=E6=B3=A8=E8=A7=A3=20?= =?UTF-8?q?=F0=9F=92=A1=20docs(readme)=EF=BC=9A=E6=B7=BB=E5=8A=A0=E7=AC=AC?= =?UTF-8?q?4=E9=A2=98=E6=95=B0=E5=AD=A6=E5=88=86=E6=9E=90=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=92=8C=E7=AE=97=E6=B3=95=E8=AF=B4=E6=98=8E=20?= =?UTF-8?q?=E2=9C=85=20test(euler=5F3.py)=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=B8=BB=E5=87=BD=E6=95=B0=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E8=B4=A8=E5=9B=A0=E6=95=B0=E5=88=86=E8=A7=A3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++ pyproject.toml | 2 +- solutions/0003.largestprime/euler_3.py | 17 +++++- solutions/0004.palindrome/eular_4.py | 52 +++++++++++++++++ solutions/0004.palindrome/readme.md | 48 ++++++++++++++++ solutions/0005.smallestMultiple/eular_5.py | 67 ++++++++++++++++++++++ solutions/0005.smallestMultiple/readme.md | 11 ++++ 7 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 solutions/0004.palindrome/eular_4.py create mode 100644 solutions/0004.palindrome/readme.md create mode 100644 solutions/0005.smallestMultiple/eular_5.py create mode 100644 solutions/0005.smallestMultiple/readme.md diff --git a/README.md b/README.md index d036a16..6cf6ec7 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,11 @@ 也会有一些新奇的想法。主要使用 Python 解决,也会有 Haskell 、 Rust 或者 Lean4 的解决方法。 项目使用 `uv` 进行管理,毕竟主要还是使用 Python来写的。 + + +----- + +**KEY POINT :** + +要把数学回归数学,而不是回归程序。 +从数学角度去考虑问题,用数学化简计算逻辑。 diff --git a/pyproject.toml b/pyproject.toml index 085c5c4..3497034 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 = [] diff --git a/solutions/0003.largestprime/euler_3.py b/solutions/0003.largestprime/euler_3.py index 7901d8c..d787a83 100644 --- a/solutions/0003.largestprime/euler_3.py +++ b/solutions/0003.largestprime/euler_3.py @@ -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} diff --git a/solutions/0004.palindrome/eular_4.py b/solutions/0004.palindrome/eular_4.py new file mode 100644 index 0000000..3ecbce9 --- /dev/null +++ b/solutions/0004.palindrome/eular_4.py @@ -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]}") diff --git a/solutions/0004.palindrome/readme.md b/solutions/0004.palindrome/readme.md new file mode 100644 index 0000000..3583ac7 --- /dev/null +++ b/solutions/0004.palindrome/readme.md @@ -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 +``` diff --git a/solutions/0005.smallestMultiple/eular_5.py b/solutions/0005.smallestMultiple/eular_5.py new file mode 100644 index 0000000..a72f9f6 --- /dev/null +++ b/solutions/0005.smallestMultiple/eular_5.py @@ -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)) diff --git a/solutions/0005.smallestMultiple/readme.md b/solutions/0005.smallestMultiple/readme.md new file mode 100644 index 0000000..58a4a20 --- /dev/null +++ b/solutions/0005.smallestMultiple/readme.md @@ -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$ 相乘