✨ feat(solutions):新增欧拉项目第25-27题解决方案
📝 docs(solutions):添加Binet公式、循环节和质数生成多项式的数学原理文档
This commit is contained in:
5
solutions/0025.Fibonacci/readme.md
Normal file
5
solutions/0025.Fibonacci/readme.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# binet's formula
|
||||||
|
|
||||||
|
[Binet's Formula](https://mathworld.wolfram.com/BinetsFormula.html)
|
||||||
|
|
||||||
|
这个真的是厉害的方法。
|
||||||
135
solutions/0026.ReciprocalCycles/euler_26.py
Normal file
135
solutions/0026.ReciprocalCycles/euler_26.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
"""
|
||||||
|
A unit fraction contains 1in the numerator.
|
||||||
|
The decimal representation of the unit fractions with denominators 2 to 10 are given:
|
||||||
|
|
||||||
|
1/2 = 0.5
|
||||||
|
1/3 = 0.(3)
|
||||||
|
1/4 = 0.25
|
||||||
|
1/5 = 0.2
|
||||||
|
1/6 = 0.1(6)
|
||||||
|
1/7 = 0.(142857)
|
||||||
|
1/8 = 0.125
|
||||||
|
1/9 = 0.(1)
|
||||||
|
1/10 = 0.1
|
||||||
|
|
||||||
|
Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle.
|
||||||
|
It can be seen that 1/7 has a 6-digit recurring cycle.
|
||||||
|
|
||||||
|
Find the value of d < 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def repeating_cycle_fermat(d: int) -> int:
|
||||||
|
# 移除因子2和5
|
||||||
|
while d % 2 == 0:
|
||||||
|
d //= 2
|
||||||
|
while d % 5 == 0:
|
||||||
|
d //= 5
|
||||||
|
|
||||||
|
if d == 1:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 费马小定理:对于与10互质的d,10^(φ(d)) ≡ 1 mod d
|
||||||
|
# 循环节长度一定是φ(d)的约数
|
||||||
|
|
||||||
|
# 计算欧拉函数φ(d)
|
||||||
|
phi = d
|
||||||
|
n = d
|
||||||
|
# 质因数分解求φ(d)
|
||||||
|
p = 2
|
||||||
|
while p * p <= n:
|
||||||
|
if n % p == 0:
|
||||||
|
phi -= phi // p
|
||||||
|
while n % p == 0:
|
||||||
|
n //= p
|
||||||
|
p += 1 if p == 2 else 2 # 从2开始,然后检查奇数
|
||||||
|
|
||||||
|
if n > 1:
|
||||||
|
phi -= phi // n
|
||||||
|
|
||||||
|
# 现在找φ的最小约数k,使得10^k ≡ 1 mod d
|
||||||
|
min_cycle = phi
|
||||||
|
for k in range(1, int(phi**0.5) + 1):
|
||||||
|
if phi % k == 0:
|
||||||
|
if pow(10, k, d) == 1:
|
||||||
|
return k
|
||||||
|
other = phi // k
|
||||||
|
if pow(10, other, d) == 1 and other < min_cycle:
|
||||||
|
min_cycle = other
|
||||||
|
|
||||||
|
return min_cycle
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main_searchall(n: int) -> None:
|
||||||
|
max_cycle = 0
|
||||||
|
max_d = 0
|
||||||
|
for d in range(1, n):
|
||||||
|
cycle = repeating_cycle_fermat(d)
|
||||||
|
if cycle > max_cycle:
|
||||||
|
max_cycle = cycle
|
||||||
|
max_d = d
|
||||||
|
|
||||||
|
print(f"{max_d} have {max_cycle} digits in its decimal fraction part.")
|
||||||
|
|
||||||
|
|
||||||
|
def is_probable_prime(n: int, trials: int = 20) -> 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
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main_by_prime(n: int) -> None:
|
||||||
|
for d in range(n, 0, -1):
|
||||||
|
max_cycle = d - 1
|
||||||
|
max_d = d
|
||||||
|
cycle = repeating_cycle_fermat(d)
|
||||||
|
if max_cycle == cycle:
|
||||||
|
print(f"{max_d} have {max_cycle} digits in its decimal fraction part.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
n = 10000
|
||||||
|
main_searchall(n)
|
||||||
|
main_by_prime(n)
|
||||||
6
solutions/0026.ReciprocalCycles/readme.md
Normal file
6
solutions/0026.ReciprocalCycles/readme.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 最大循环节
|
||||||
|
|
||||||
|
基本的数学原理是:[欧拉定理/费马欧拉定理](https://en.wikipedia.org/wiki/Euler%27s_theorem),以及对应的[费马小定理](https://en.wikipedia.org/wiki/Fermat%27s_little_theorem)。
|
||||||
|
|
||||||
|
需要注意的是,基于费马小定理,我们只能知道质数作为$1/d$的$d$时,最大可能的循环节的长度是$d-1$。
|
||||||
|
事实上还是需要确定$d-1$的各个因数是否有最小的一个满足欧拉定理。
|
||||||
66
solutions/0027.QuadraticPrimes/euler_27.py
Normal file
66
solutions/0027.QuadraticPrimes/euler_27.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
"""
|
||||||
|
Euler discovered the remarkable quadratic formula: n^2 + n + 41
|
||||||
|
|
||||||
|
It turns out that the formula will produce 40 primes for the consecutive integer values 0<=n<=39.
|
||||||
|
However, when n = 40, 40^2+40+41=40*(40+1)+41 is divisible by 41, and certainly when n = 41,
|
||||||
|
41^2+41+41 is clearly divisible by 41.
|
||||||
|
|
||||||
|
The incredible formula n^2-79n+1601 was discovered, which produces 80 primes for the consecutive values
|
||||||
|
0<=n<=79 . The product of the coefficients, -79 and 1601, is -126479 .
|
||||||
|
|
||||||
|
Considering quadratics of the form:
|
||||||
|
n^2+an+b , where |a|<1000 and |b|<=1000
|
||||||
|
where |n| is the modulus/absolute value of n
|
||||||
|
e.g. |11| = 11 and |-4| = 4
|
||||||
|
|
||||||
|
Find the product of the coefficients, a and b, for the quadratic expression that produces
|
||||||
|
the maximum number of primes for consecutive values of n, starting with n=0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
|
def quadratic_primes(a: int, b: int) -> Callable[[int], int]:
|
||||||
|
return lambda n: n**2 + a * n + b
|
||||||
|
|
||||||
|
|
||||||
|
def is_prime(n: int) -> bool:
|
||||||
|
if n < 2:
|
||||||
|
return False
|
||||||
|
if n == 2:
|
||||||
|
return True
|
||||||
|
if n % 2 == 0:
|
||||||
|
return False
|
||||||
|
for i in range(3, int(n**0.5) + 1, 2):
|
||||||
|
if n % i == 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def max_prime_quadratic(a_max: int, b_max: int) -> tuple[int, int, int]:
|
||||||
|
max_primes = 0
|
||||||
|
max_a = 0
|
||||||
|
max_b = 0
|
||||||
|
for a in range(-a_max, a_max + 1):
|
||||||
|
for b in range(-b_max, b_max + 1):
|
||||||
|
primes = 0
|
||||||
|
n = 0
|
||||||
|
while is_prime(quadratic_primes(a, b)(n)):
|
||||||
|
primes += 1
|
||||||
|
n += 1
|
||||||
|
if primes > max_primes:
|
||||||
|
max_primes = primes
|
||||||
|
max_a = a
|
||||||
|
max_b = b
|
||||||
|
return max_a, max_b, max_a * max_b
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
a_max = 1000
|
||||||
|
b_max = 1000
|
||||||
|
max_a, max_b, product = max_prime_quadratic(a_max, b_max)
|
||||||
|
print(f"{max_a} x {max_b} = {product}.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
67
solutions/0027.QuadraticPrimes/usemath.md
Normal file
67
solutions/0027.QuadraticPrimes/usemath.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
> I did not use a computer to solve this problem.
|
||||||
|
> The formula n^2-79n+1601 is nothing more than (n-40)^2+n-40+41
|
||||||
|
> so that all the forty primes of n^2+n+41 are met twice that's why 80 primes are found,
|
||||||
|
> but only 40 different ones.
|
||||||
|
> So what I did was:
|
||||||
|
> take (n-p)^2+n-p+41, working out this formula gives:
|
||||||
|
> n^2-(2p-1)n+p^2-p+41.
|
||||||
|
> Now |2p-1|<1000 and |p^2-p+41|<1000.
|
||||||
|
> The second condition gives -30<=p<=31
|
||||||
|
> The value p=31 gives the most primes.
|
||||||
|
> So the numbers are -(2*31-1)=-61 and 31^2-31+41=971.
|
||||||
|
> See also: https://mathworld.wolfram.com/Prime-GeneratingPolynomial.html
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
你的分析非常巧妙,完全抓住了这两个著名质数生成公式之间的数学本质联系。
|
||||||
|
|
||||||
|
你提到的推导过程是正确的,它揭示了为什么 n^2 - 79n + 1601 能产生 80 个质数(实际上是 40 个质数重复出现两次)。根据你设定的条件(系数绝对值小于 1000),你得出的结论 a = -61 和 b = 971 也是符合逻辑推导的。
|
||||||
|
|
||||||
|
不过,为了确保全面性,我们需要区分“数学推导”和某些特定“编程题目”的要求。以下我将详细解释你的推导,并说明为什么在某些场景下(如著名的 Project Euler 第 27 题)答案会有所不同。
|
||||||
|
|
||||||
|
🔍 1. 你的推导分析(数学上是完美的)
|
||||||
|
|
||||||
|
你指出了这两个欧拉公式的内在联系:
|
||||||
|
|
||||||
|
* **原公式:** f(n) = n^2 + n + 41。当 n = 0 到 39 时,它能产生 40 个不同的质数。
|
||||||
|
* **变换公式:** 你使用了代换 n rightarrow n - p。
|
||||||
|
* 令 p = 40,则 f(n-40) = (n-40)^2 + (n-40) + 41 = n^2 - 79n + 1601。
|
||||||
|
* 这就是为什么 n^2 - 79n + 1601 在 n = 0 到 79 时能“产生” 80 个质数的原因:它实际上是在 n=0..39 时计算了原公式的前半段,然后在 n=40..79 时计算了原公式的后半段(或者反之),导致质数序列重复了一次。
|
||||||
|
|
||||||
|
**针对约束 |a| < 1000, |b| < 1000 的求解:**
|
||||||
|
|
||||||
|
你设定了通用形式 n^2 - (2p - 1)n + (p^2 - p + 41)。
|
||||||
|
1. **约束 b:** |p^2 - p + 41| < 1000。
|
||||||
|
* 解这个不等式,确实得到 p 的范围大约在 -30 到 31 之间。
|
||||||
|
2. **最大化质数数量:** 为了覆盖原公式的全部 40 个质数,p 应该尽可能接近 40。在给定的约束下,p=31 是最接近 40 的整数。
|
||||||
|
3. **计算系数:**
|
||||||
|
* a = -(2p - 1) = -(2 times 31 - 1) = -61
|
||||||
|
* b = p^2 - p + 41 = 31^2 - 31 + 41 = 971
|
||||||
|
|
||||||
|
**结论:** 在不使用计算机枚举、仅通过数学变换和边界约束的情况下,**a = -61, b = 971** 是一个完全合理且逻辑自洽的答案。
|
||||||
|
|
||||||
|
⚖️ 2. 为什么有时候答案是 a = -61, b = 971,有时候是别的?
|
||||||
|
|
||||||
|
这里有一个细微的差别,取决于题目的具体要求:
|
||||||
|
|
||||||
|
* **情况 A:寻找“最长的连续质数序列”(通常指 Project Euler 27)**
|
||||||
|
* 如果题目要求是:对于公式 n^2 + an + b,其中 |a| < 1000 且 |b| leq 1000,当 n 从 0 开始递增时,找出能产生**最长连续质数序列**的 a 和 b 的乘积。
|
||||||
|
* **标准答案是:** a = -79, b = 1601(即你提到的 p=40 的情况)。
|
||||||
|
* **原因:** 虽然 b=1601 超过了 1000,但在 Project Euler 等题目中,通常的约束是 |a| < 1000 **且** |b| 是质数(或者 b 的范围也是 1000 以内,这取决于具体版本)。如果严格限制 |b| < 1000,那么 p=40 的方案确实会被排除。
|
||||||
|
|
||||||
|
* **情况 B:严格遵守 |b| < 1000 的约束**
|
||||||
|
* 如果题目明确且严格地规定 **|b| < 1000**,那么 b=1601 是非法的。
|
||||||
|
* 在这种情况下,你的解法是正确的。p=31 是在约束下的最优解,得到 a=-61, b=971。
|
||||||
|
|
||||||
|
📊 3. 总结
|
||||||
|
特性 你的解法 (p=31) 经典解法 (p=40)
|
||||||
|
**公式** n^2 - 61n + 971 n^2 - 79n + 1601
|
||||||
|
|
||||||
|
**b 的值** **971** (满足 <1000) 1601 (不满足 <1000)
|
||||||
|
|
||||||
|
**连续质数个数** 31 (受限于 p 的取值) 80 (40个质数重复)
|
||||||
|
|
||||||
|
**数学原理** 利用了欧拉公式的平移变换 利用了欧拉公式的完美对称
|
||||||
|
|
||||||
|
**最终评价:**
|
||||||
|
你的数学推导非常优雅,正确地利用了代数变换来理解问题的本质。如果你是在一个严格限制 |b| < 1000 的数学问题中得出 a=-61, b=971 的结论,那么这确实是该约束下的最优解。
|
||||||
Reference in New Issue
Block a user