diff --git a/solutions/0039.IntegerTriangles/README.md b/solutions/0039.IntegerTriangles/README.md new file mode 100644 index 0000000..6c50cc2 --- /dev/null +++ b/solutions/0039.IntegerTriangles/README.md @@ -0,0 +1,105 @@ +# 勾股三元组 + +以下为AI总结。 + +--- + +### 1. 核心定义 + +**勾股三元组** 是指三个**正整数** \((a, b, c)\),满足**勾股定理**方程: +\[ +a^2 + b^2 = c^2 +\] +其中 \(c\) 称为**斜边**,\(a\) 和 \(b\) 称为**直角边**。 + +**例如**:\((3, 4, 5)\),因为 \(3^2 + 4^2 = 9 + 16 = 25 = 5^2\)。 + +--- + +### 2. 关键分类:本原 vs 非本原 + +#### **本原勾股三元组** +- **定义**:满足 \(\gcd(a, b, c) = 1\) 的勾股三元组。即三个数互质(最大公约数为1)。 +- **性质**: + 1. \(a\) 和 \(b\) 必然**一奇一偶**。 + 2. \(c\) 永远是奇数。 + 3. 任意两个数互质。 +- **例子**:\((3, 4, 5)\), \((5, 12, 13)\), \((8, 15, 17)\)。 +- **重要性**:所有勾股三元组的“基本生成元”,非本原三元组都是本原三元组的正整数倍缩放。 + +#### **非本原(导出)勾股三元组** +- **定义**:由一个本原三元组乘以一个大于1的正整数 \(k\) 得到,即 \((ka, kb, kc)\),此时 \(\gcd(a, b, c) > 1\)。 +- **例子**:\((6, 8, 10)\) 是 \((3, 4, 5)\) 的2倍。 + +--- + +### 3. 核心生成公式(欧几里得公式) + +这是一个在数论中用于**生成所有本原勾股三元组**的经典公式。 + +给定任意一对**互质**的正整数 \(m\) 和 \(n\) (\(m > n > 0\)),且满足: +- \(m\) 和 \(n\) **一奇一偶**(即不同时为奇数,也不同时为偶数)。 + +则可生成一个本原勾股三元组: +\[ +\begin{align*} +a &= m^2 - n^2 \\ +b &= 2mn \\ +c &= m^2 + n^2 +\end{align*} +\] + +**推导思路**: +从方程 \(a^2 + b^2 = c^2\) 出发,进行代数变形。一种常见方法是将方程重写为: +\[ +c^2 - a^2 = b^2 \quad \Rightarrow \quad (c-a)(c+a) = b^2 +\] +通过设定 \(\frac{c+a}{b} = \frac{m}{n}\)(既约分数),并引入参数 \(m, n\),经过整理即可得到上述公式。它本质上是利用了复数模的性质:\(|m + ni|^2 = m^2 + n^2\)。 + +**例子**:取 \(m=2, n=1\)(互质,一奇一偶) +\[ +a = 4-1=3, \quad b = 2\times2\times1=4, \quad c = 4+1=5 \quad \Rightarrow (3,4,5) +\] +取 \(m=3, n=2\) +\[ +a=9-4=5, \quad b=12, \quad c=9+4=13 \quad \Rightarrow (5,12,13) +\] + +**重要结论**:**所有本原勾股三元组都可以由唯一一对满足条件的 \((m, n)\) 通过此公式生成**。所有勾股三元组(包括非本原)则是本原三元组的正整数倍。 + +--- + +### 4. 几何视角与三角学联系 + +在单位圆 \(x^2 + y^2 = 1\) 上,有理点 \(\left( \frac{a}{c}, \frac{b}{c} \right)\) 对应于一个勾股三元组 \((a, b, c)\)。 +通过参数化 \(t = \tan(\theta/2)\)(**半角正切公式**),令 \(t = \frac{n}{m}\)(有理数),则: +\[ +\cos\theta = \frac{1-t^2}{1+t^2} = \frac{m^2-n^2}{m^2+n^2} = \frac{a}{c}, \quad \sin\theta = \frac{2t}{1+t^2} = \frac{2mn}{m^2+n^2} = \frac{b}{c} +\] +这直接给出了欧几里得公式的三角解释。因此,寻找勾股三元组等价于寻找单位圆上的有理点。 + +--- + +### 5. 一些有趣的数学性质 + +1. **面积**:本原三元组生成的直角三角形的面积 \(S = \frac{1}{2}ab = mn(m-n)(m+n)\),它总是**整数**,并且是6的倍数。 +2. **周长**:本原三元组的周长 \(P = a+b+c = 2m(m+n)\)。 +3. **斜边性质**: + - 本原三元组的斜边 \(c\) 是奇数,且可以表示为两个平方数之和 \(c = m^2 + n^2\)。 + - 斜边 \(c\) 的**每个**形如 \(4k+1\)(\(k\)为正整数)的质因数,都对应着一种将该斜边表示为两个平方数之和的方式,从而关联到更深刻的**费马平方和定理**。 +4. **无限性**:存在无限多个本原勾股三元组。 + +--- + +### 6. 历史与意义 + +- 最早的记录可追溯到公元前1800年左右的**古巴比伦**泥板(如普林顿322号),其上刻有大量勾股三元组,表明古人已掌握其计算规律。 +- 在古希腊,**毕达哥拉斯学派**对其进行了系统研究,并将其与几何和“完美数”观念联系。 +- 在近代数学中,对勾股三元组的研究自然导向了**费马大定理**(方程 \(a^n + b^n = c^n\) 在 \(n>2\) 时无正整数解),这是数论中的一个里程碑。 +- 在现代,勾股三元组出现在密码学、算法设计和一些物理问题中。 + +--- + +### 总结 + +勾股三元组是满足 \(a^2 + b^2 = c^2\) 的正整数三元组。其核心是**本原三元组**,可通过**欧几里得公式** \((m^2-n^2, 2mn, m^2+n^2)\) 用一对互质且一奇一偶的参数 \((m, n)\) **完全生成**。它在数学上连接了几何(直角三角形)、数论(平方和、质因数分解)和代数(曲线有理点),是一个基础而内涵丰富的概念。 diff --git a/solutions/0039.IntegerTriangles/euler_39.py b/solutions/0039.IntegerTriangles/euler_39.py index 69bb508..daca5b4 100644 --- a/solutions/0039.IntegerTriangles/euler_39.py +++ b/solutions/0039.IntegerTriangles/euler_39.py @@ -6,3 +6,98 @@ If p is the perimeter of a right angle triangle with integral length sides, For which value of p<=1000, is the number of solutions maximised? """ + +import time +import math + + +def timer(func): + def wrapper(*args, **kwargs): + start_time = time.time() + result = func(*args, **kwargs) + end_time = time.time() + print(f"{func.__name__} took time: {end_time - start_time:.6f} seconds") + return result + return wrapper + + +def generate_pythagorean_triples(max_perimeter: int = 1000) : + """ + 使用yield生成勾股三元组的m和n参数 + + 参数: + max_perimeter: 三角形周长的最大值 + + 生成: + (m, n) 元组,满足: + - m > n > 0 + - m和n互质 (gcd(m, n) == 1) + - m和n一奇一偶 + - 三角形周长 2*m*(m+n) < max_perimeter + """ + # 从m=2开始(因为m>n≥1) + m = 2 + + while True: + # 对于每个m,n从1到m-1 + for n in range(1, m): + # 检查是否满足互质条件 + if math.gcd(m, n) != 1: + continue + + # 检查是否一奇一偶 + if (m % 2) == (n % 2): + continue + + # 计算对应三角形的周长 + perimeter = 2 * m * (m + n) + + # 如果周长超过限制,则停止生成 + if perimeter >= max_perimeter: + # 对于当前m,如果最小周长(当n=1时)已经超过限制, + # 那么后续更大的m也会超过,所以完全停止 + if n == 1: + return + + # 如果周长在限制内,则yield当前(m, n) + if perimeter < max_perimeter: + yield (m, n) + + m += 1 + + +def triangle_length(m:int, n:int) -> int: + return 2 * m * (m + n) + +def get_abc(m:int, n:int) -> tuple[int, int, int]: + a = m**2 - n**2 + b = 2 * m * n + c = m**2 + n**2 + return a, b, c + +def primitive_triangles(limit: int = 1000) -> dict[int, list] : + triangle_lengths = {} + for m, n in generate_pythagorean_triples(limit): + length = triangle_length(m, n) + if length not in triangle_lengths: + triangle_lengths[length] = set() + a,b,c = get_abc(m, n) + triangle_lengths[length].add((a, b, c)) + for k in range(2, limit // length + 1) : + lenp = k * (a + b + c) + if lenp not in triangle_lengths: + triangle_lengths[lenp] = set() + triangle_lengths[lenp].add((a*k, b*k, c*k)) + return triangle_lengths + + +@timer +def main(): + max_perimeter = 1000 + triangle_lengths = primitive_triangles(max_perimeter) + print(triangle_lengths) + max_length = max(triangle_lengths, key=lambda x: len(triangle_lengths[x])) + print(f"Max length: {max_length}, Count: {len(triangle_lengths[max_length])}") + +if __name__ == "__main__": + main() diff --git a/solutions/0040.ChampernownesConstant/euler_40.py b/solutions/0040.ChampernownesConstant/euler_40.py new file mode 100644 index 0000000..7285a5a --- /dev/null +++ b/solutions/0040.ChampernownesConstant/euler_40.py @@ -0,0 +1,91 @@ +""" +An irrational decimal fraction is created by concatenating the positive integers: + 0.123456789101112131415161718192021... + +It can be seen that the 12th digit of the fractional part is 1. + +If d_n represents the nth digit of the fractional part, find the value of the following expression. + + d_1 × d_10 × d_100 × d_1000 × d_10000 × d_100000 × d_1000000 +""" + +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__} time: {end_time - start_time:.6f} seconds") + return result + return wrapper + +class ChampernownesConstant: + def __init__(self): + self._cache = "123456789" + self._level = 1 + self._nth = 1 + + def __iter__(self): + if self._nth > len(self._cache): + self._level += 1 + self._cache += "".join(str(i) for i in range(10**(self._level-1), 10**self._level)) + yield self._cache[self._nth-1] + self._nth += 1 + + def __next__(self): + return next(self.__iter__()) + + def get_nth_digit(self, nth:int) -> int : + while True: + if nth > len(self._cache): + self._level += 1 + self._cache += "".join(str(i) for i in range(10**(self._level-1), 10**self._level)) + else: + return int(self._cache[nth-1]) + + +@timer +def main_longstring() -> None: + gen = ChampernownesConstant() + ziel = [1,10,100,1000,10000,100000,1000000] + res = 1 + for i in ziel: + res *= gen.get_nth_digit(i) + print(res) + + +def length_numbers(k:int) -> int : + """计算前k位数的总位数""" + if k == 0: + return 0 + return (10**k * (9*k - 1) + 1) // 9 + + +def get_nth_digit(nth:int) -> int : + if nth < 0 : + raise ValueError("nth must be non-negative") + level = 1 + while length_numbers(level) < nth : + level += 1 + # 计算在k位数中的偏移量 + offset = nth - length_numbers(level-1) # 在k位数中的第几个数字 + num_index = (offset - 1) // level # 第几个k位数(从0开始) + num = 10**(level-1) + num_index # 具体的数字 + digit_index = (offset - 1) % level # 在数字中的位置 + + return int(str(num)[digit_index]) + + +@timer +def main_quick() -> None : + ziel = [1,10,100,1000,10000,100000,1000000] + res = 1 + for i in ziel: + res *= get_nth_digit(i) + print(res) + + +if __name__ == "__main__": + main_longstring() + main_quick()