feat(0052): 添加欧拉52题“排列倍数”的最优算法实现

This commit is contained in:
2026-03-23 11:12:24 +08:00
parent be30320454
commit 17064cf38c
2 changed files with 147 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
欧拉计划第52题**排列倍数**)的答案是 **142857**
最快的数学计算方式基于以下**三大核心数学原理**的级联优化:
---
### 1. 位数约束原理(范围剪枝)
**数学基础**:若 $x$ 与 $6x$ 位数相同,则必须满足:
$$10^{n-1} \leq x < \frac{10^n}{6} \approx 1.666... \times 10^{n-1}$$
这直接排除了 $60\%$ 以上的搜索空间例如6位数时只需检查 $[100000, 166666)$ 区间而非 $[100000, 1000000)$。
---
### 2. 模9同余原理整除筛选
**数学基础**数字和与模9同余数根理论)。 $x$ $kx$ 是数字排列
$$\sum \text{digits}(x) \equiv x \pmod{9} \equiv kx \equiv \sum \text{digits}(kx) \pmod{9}$$
由此推出 $(k-1)x \equiv 0 \pmod{9}$。 $k=2$,得:
$$x \equiv 0 \pmod{9}$$
**关键优化**只需检查9的倍数搜索空间再缩减 $89\%$。
---
### 3. 规范形签名原理(排列判定)
**数学基础**两个数是排列 它们的**数字频数向量**Digit Histogram相同
不采用字符串排序$O(d \log d)$而是用**计数数组**作为规范签名$O(d)$
```python
def signature(n):
count = [0] * 10
while n:
count[n % 10] += 1
n //= 10
return tuple(count) # 如142857 → (1,1,1,1,1,1,0,1,0,0)的某种编码
```
---
### 最优算法实现Python
```python
def euler52():
n = 1
while True:
start = 10**(n-1)
# 核心约束x * 6 必须仍是n位数
end = int(10**n / 6) + 1
# 步长为9利用模9同余原理
for x in range((start + 8) // 9 * 9, end, 9):
sig = signature(x)
# 检查2x到6x是否共享相同签名
if all(signature(k * x) == sig for k in range(2, 7)):
return x
n += 1
# 结果142857即1/7的小数循环节
```
**复杂度**对于 $n$ 位数候选量从 $9 \times 10^{n-1}$ 降至约 $0.074 \times 10^{n-1}$配合 $O(n)$ 的签名比较可在**微秒级**求解
---
### 深层数学结构
**142857** 并非偶然它是**循环数**Cyclic Number $1/7 = 0.\overline{142857}$ 的小数展开密切相关其性质源于 $10$ 是模 $7$ 的原根导致 $2/7, 3/7...6/7$ 的循环节恰好是 $142857$ 的轮换排列