Files
SolutionEuler/solutions/0052.PermutedMultiples/README.md

2.4 KiB
Raw Blame History

欧拉计划第52题排列倍数)的答案是 142857

最快的数学计算方式基于以下三大核心数学原理的级联优化:


1. 位数约束原理(范围剪枝)

数学基础:若 x6x 位数相同,则必须满足:

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同余数根理论。若 xkx 是数字排列,则:

\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)$

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

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 Number1/7 = 0.\overline{142857} 的小数展开密切相关。其性质源于 10 是模 7 的原根,导致 2/7, 3/7...6/7 的循环节恰好是 142857 的轮换排列。