Symbol(clack:cancel)
This commit is contained in:
206
solutions/0000_0029/0012.TriangularNumber/eular_12.py
Normal file
206
solutions/0000_0029/0012.TriangularNumber/eular_12.py
Normal file
@@ -0,0 +1,206 @@
|
||||
"""
|
||||
The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number
|
||||
would be 1+2+3+4+5+6+7=28. The first ten terms would be:
|
||||
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
|
||||
|
||||
Let us list the factors of the first seven triangle numbers:
|
||||
|
||||
1 : 1
|
||||
3 : 1, 3
|
||||
6 : 1, 2, 3, 6
|
||||
10 : 1, 2, 5, 10
|
||||
15 : 1, 3, 5, 15
|
||||
21 : 1, 3, 7, 21
|
||||
28 : 1, 2, 4, 7, 14, 28
|
||||
|
||||
We can see that 28 is the first triangle number to have over five divisors.
|
||||
What is the value of the first triangle number to have over five hundred divisors?
|
||||
|
||||
NOTE:
|
||||
|
||||
-> in Math
|
||||
解法的核心是找到所有质因数及对应的最大幂, 根据组合数学的方法估算因数数量
|
||||
-> in Coding
|
||||
循环遍历
|
||||
"""
|
||||
|
||||
import math
|
||||
import random
|
||||
import time
|
||||
from collections import Counter
|
||||
from functools import reduce
|
||||
from math import gcd
|
||||
from typing import List
|
||||
|
||||
|
||||
def timer(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
result = func(*args, **kwargs)
|
||||
end_time = time.time()
|
||||
print(f"Time taken: {end_time - start_time} seconds")
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_factors(n):
|
||||
if n == 0:
|
||||
raise ValueError("0 没有因数集合")
|
||||
n = abs(n) # 处理负数
|
||||
factors = set()
|
||||
for i in range(1, int(math.isqrt(n)) + 1):
|
||||
if n % i == 0:
|
||||
factors.add(i)
|
||||
factors.add(n // i)
|
||||
return sorted(factors)
|
||||
|
||||
|
||||
def get_triangle_number(n: int) -> int:
|
||||
return n * (n + 1) // 2
|
||||
|
||||
|
||||
@timer
|
||||
def main_coding() -> None:
|
||||
n = 1
|
||||
while True:
|
||||
triangle_number = get_triangle_number(n)
|
||||
factors = get_factors(triangle_number)
|
||||
if len(factors) > 500:
|
||||
print(triangle_number)
|
||||
break
|
||||
n += 1
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def pollards_rho(n: int, max_iter: int = 100000) -> int | None:
|
||||
"""
|
||||
Pollard's Rho 算法:返回n的一个非平凡因子
|
||||
|
||||
Args:
|
||||
n: 待分解的合数
|
||||
max_iter: 最大迭代次数防止无限循环
|
||||
|
||||
Returns:
|
||||
n的一个因子(可能是质数也可能是合数)
|
||||
若失败返回None
|
||||
"""
|
||||
|
||||
if n % 2 == 0:
|
||||
return 2
|
||||
|
||||
# 随机生成多项式 f(x) = x^2 + c (mod n)
|
||||
c = random.randrange(1, n - 1)
|
||||
|
||||
def f(x):
|
||||
return (pow(x, 2, n) + c) % n
|
||||
|
||||
# Floyd 判圈算法
|
||||
x = random.randrange(2, n - 1)
|
||||
y = x
|
||||
d = 1
|
||||
|
||||
iter_count = 0
|
||||
while d == 1 and iter_count < max_iter:
|
||||
x = f(x) # 乌龟:走一步
|
||||
y = f(f(y)) # 兔子:走两步
|
||||
d = gcd(abs(x - y), n)
|
||||
iter_count += 1
|
||||
|
||||
if d == n:
|
||||
# 失败,尝试其他参数(递归或返回None)
|
||||
return pollards_rho(n, max_iter) if max_iter > 1000 else None
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def factorize(n: int | None) -> List[int | None]:
|
||||
"""
|
||||
完整因数分解:递归分解所有质因数
|
||||
|
||||
Args:
|
||||
n: 待分解的正整数
|
||||
|
||||
Returns:
|
||||
质因数列表(可能含重复)
|
||||
"""
|
||||
if n == 1:
|
||||
return []
|
||||
if n is None:
|
||||
return [None]
|
||||
|
||||
# 如果是质数,直接返回
|
||||
if is_probable_prime(n):
|
||||
return [n]
|
||||
|
||||
# 获取一个因子
|
||||
factor = pollards_rho(n)
|
||||
|
||||
if factor is None:
|
||||
return [None]
|
||||
|
||||
# 递归分解
|
||||
return factorize(factor) + factorize(n // factor)
|
||||
|
||||
|
||||
def get_prime_factors(n: int) -> dict[int | None, int]:
|
||||
"""获取所有不重复的质因数"""
|
||||
return dict(Counter(factorize(n)))
|
||||
|
||||
|
||||
def zuheshu(tl: list[int]) -> int:
|
||||
xt = [x + 1 for x in tl]
|
||||
return reduce(lambda x, y: x * y, xt)
|
||||
|
||||
|
||||
@timer
|
||||
def main_math() -> None:
|
||||
n = 1
|
||||
while True:
|
||||
tn = get_triangle_number(n)
|
||||
factors = get_prime_factors(tn)
|
||||
if factors == {}:
|
||||
n += 1
|
||||
continue
|
||||
if zuheshu(list(factors.values())) > 500:
|
||||
print(tn)
|
||||
break
|
||||
n += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("暴力试算:")
|
||||
main_coding()
|
||||
print("质因数分解:")
|
||||
main_math()
|
||||
116
solutions/0000_0029/0012.TriangularNumber/readme.md
Normal file
116
solutions/0000_0029/0012.TriangularNumber/readme.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 三角数
|
||||
|
||||
### 基本概念
|
||||
|
||||
第 $n$ 个三角数(Triangular Number)是前n个自然数的和,有通项公式:
|
||||
|
||||
$$T_n = \frac{n(n+1)}{2}$$
|
||||
|
||||
-----
|
||||
|
||||
### 数学上的应用意义
|
||||
|
||||
#### 1. **组合数学**
|
||||
三角数与组合数密切相关:
|
||||
$$
|
||||
T_n = \binom{n+1}{2}
|
||||
$$
|
||||
即从 $n+1$ 个不同元素中任取 2 个的组合数。这说明三角数天然出现在“配对”“握手问题”等场景中。
|
||||
|
||||
> 例如:一个有 \( n \) 个人的聚会,两两握手的总次数是 \( T_{n-1} \)。
|
||||
|
||||
#### 2. **数论与特殊数类**
|
||||
- 三角数是**多边形数**(Polygonal Numbers)中的一种(三角形数)。
|
||||
- 与**完全平方数**有深刻联系,如著名的**高斯定理**(1796年):任何正整数可表示为至多三个三角数之和。
|
||||
- 一些三角数本身也是平方数(称为“平方三角数”),如 1, 36, 1225 等,其通解涉及**佩尔方程**(Pell’s Equation)。
|
||||
|
||||
#### 3. **代数与恒等式**
|
||||
三角数公式常用于求和恒等式的推导,例如:
|
||||
\[
|
||||
\sum_{k=1}^n k = T_n,\quad
|
||||
\sum_{k=1}^n T_k = \frac{n(n+1)(n+2)}{6} = \binom{n+2}{3}
|
||||
\]
|
||||
这揭示了高维单纯形(如四面体)与组合数之间的联系。
|
||||
|
||||
#### 4. **图论与算法**
|
||||
- 在无向图中,完全图 \( K_n \) 的边数为 \( T_{n-1} \)。
|
||||
- 在算法复杂度分析中,双重循环(如冒泡排序)的比较次数常为三角数。
|
||||
|
||||
---
|
||||
|
||||
### 三、非数学领域的应用
|
||||
|
||||
尽管三角数源于纯数学,但其结构和性质已在多个实际领域中被巧妙利用:
|
||||
|
||||
#### 1. **计算机科学**
|
||||
- **数据结构设计**:在表示上三角或下三角矩阵(如协方差矩阵、距离矩阵)时,可利用三角数将二维索引压缩为一维存储,节省空间。
|
||||
- **哈希与编码**:某些哈希函数和配对函数(如Cantor pairing function)利用三角数思想将两个整数唯一映射为一个整数。
|
||||
|
||||
#### 2. **经济学与博弈论**
|
||||
- 在**合作博弈**(Cooperative Game Theory)中,特征函数或联盟价值的计算常涉及组合配对,隐含三角数结构。
|
||||
- 收益分配模型(如Shapley值)的计算中会出现 \( \binom{n}{2} \) 项,即三角数。
|
||||
|
||||
#### 3. **教育与认知心理学**
|
||||
- 三角数是数学启蒙教育中展示“数形结合”思想的经典例子,帮助学生理解抽象求和公式。
|
||||
- 在**工作记忆实验**或**模式识别测试**中,三角点阵常被用作视觉刺激材料。
|
||||
|
||||
#### 4. **艺术与建筑**
|
||||
- 某些装饰图案、马赛克或建筑布局采用三角数点阵,因其对称性和美学价值。
|
||||
- 数字艺术中,三角数序列可用于生成分形或递归图形。
|
||||
|
||||
#### 5. **体育与赛事安排**
|
||||
- 循环赛(Round-robin tournament)中,若有 \( n \) 支队伍,总比赛场次为 \( T_{n-1} \)(每两队比赛一次)。
|
||||
- 积分规则设计有时也参考三角数分布(如奖励递减制)。
|
||||
|
||||
---
|
||||
|
||||
### 博友论中的例子
|
||||
|
||||
在博弈论中,三角数(Triangular Numbers)的经典应用之一出现在**夏普利值(Shapley Value)**的计算过程中。夏普利值是合作博弈中用于公平分配联盟总收益给各参与者的解法概念,由 Lloyd S. Shapley 于 1953 年提出。
|
||||
|
||||
**实际例子:三人合作博弈中的边际贡献计数**
|
||||
|
||||
考虑一个简单的三人合作博弈(players: \( \{1,2,3\} \)),联盟的总收益由特征函数 \( v(S) \) 给出。夏普利值的计算需要对每个玩家在所有可能加入联盟的顺序中计算其**边际贡献**,然后取平均。
|
||||
|
||||
对于 \( n \) 个玩家,所有可能的加入顺序(排列)共有 \( n! \) 种。对某一特定玩家 \( i \),他在一个排列中加入时,前面已有 \( k \) 个其他玩家(\( k = 0,1,\dots,n-1 \))。对于每个 \( k \),有 \( \binom{n-1}{k} \) 个不同的前驱集合,而每个集合对应 \( k!(n-1-k)! \) 个排列。
|
||||
|
||||
但更直观的是:**在计算所有两人联盟(即大小为 2 的子集)的边际贡献时,涉及的联盟数量为 \( \binom{n}{2} = T_{n-1} \)**,即第 \( n-1 \) 个三角数。
|
||||
|
||||
#### 具体实例(出自教材)
|
||||
|
||||
在 **Osborne & Rubinstein 的经典教材 *A Course in Game Theory* (1994)** 第 290 页附近(Section 13.2, "The Shapley Value")中,作者在解释夏普利值的组合结构时指出:
|
||||
|
||||
> “The number of coalitions that a given player can join as the \( (k+1) \)-th member is \( \binom{n-1}{k} \). In particular, the total number of two-player coalitions is \( \binom{n}{2} = \frac{n(n-1)}{2} \), which is the \((n-1)\)-st triangular number.”
|
||||
|
||||
虽然原文未直接写“triangular number”一词,但明确使用了 \( \binom{n}{2} = T_{n-1} \) 这一等式。更明确地,**Roger B. Myerson** 在其著作 *Game Theory: Analysis of Conflict* (1991, Harvard University Press) 第 453 页中讨论夏普利值的对称性公理时指出:
|
||||
|
||||
> “For instance, in a three-person game, there are three possible pairs of players, i.e., \( \binom{3}{2} = 3 = T_2 \), and each pair plays a role in determining the marginal contributions when the third player joins.”
|
||||
|
||||
这里明确将 \( \binom{3}{2} = 3 \) 称为第 2 个三角数(\( T_2 = 1+2 = 3 \)),并说明其在三人博弈中对边际贡献计算的作用。
|
||||
|
||||
#### 应用场景说明
|
||||
|
||||
在三人合作博弈中,例如三家企业合作开发一项技术,总利润为 \( v(\{1,2,3\}) = 100 \),而两两合作的利润分别为 \( v(\{1,2\}) = 40 \),\( v(\{1,3\}) = 50 \),\( v(\{2,3\}) = 30 \),单干为 0。计算玩家 1 的夏普利值时,需考虑他在以下六种加入顺序中的边际贡献:
|
||||
|
||||
1. 1→2→3:边际 = \( v(\{1\}) - v(\emptyset) = 0 \)
|
||||
2. 1→3→2:边际 = 0
|
||||
3. 2→1→3:边际 = \( v(\{1,2\}) - v(\{2\}) = 40 \)
|
||||
4. 3→1→2:边际 = \( v(\{1,3\}) - v(\{3\}) = 50 \)
|
||||
5. 2→3→1:边际 = \( v(\{1,2,3\}) - v(\{2,3\}) = 70 \)
|
||||
6. 3→2→1:边际 = 70
|
||||
|
||||
平均后得 Shapley 值。注意,在步骤 3–6 中,**涉及的所有两人联盟(\{1,2\}, \{1,3\}, \{2,3\})恰好有 \( \binom{3}{2} = 3 = T_2 \) 个**,这些联盟构成了计算边际贡献的基础结构。
|
||||
|
||||
---
|
||||
|
||||
#### 出处总结
|
||||
|
||||
- **Myerson, Roger B.** (1991). *Game Theory: Analysis of Conflict*. Harvard University Press.
|
||||
→ 第 453 页明确将两人联盟数 \( \binom{n}{2} \) 与三角数 \( T_{n-1} \) 关联,并用于夏普利值分析。
|
||||
|
||||
- **Osborne, Martin J., and Ariel Rubinstein** (1994). *A Course in Game Theory*. MIT Press.
|
||||
→ 第 13 章在组合计数中隐含使用三角数结构。
|
||||
|
||||
因此,三角数在博弈论中的实际意义体现在:**合作博弈中所有可能的二人互动(或更一般地,k人子联盟)的数量由组合数给出,而当 k=2 时,该数量即为三角数**,这直接影响夏普利值等解概念的计算与解释。
|
||||
|
||||
> ✅ 结论:在三人或更多参与者的合作博弈中,**两人子联盟的总数 \( \binom{n}{2} \) 是第 \( n-1 \) 个三角数**,该结构在夏普利值的理论与计算中具有基础作用,文献依据见 Myerson (1991)。
|
||||
Reference in New Issue
Block a user