From 8ee312ac1ddfdd6472cf0a96aefe56913b574401 Mon Sep 17 00:00:00 2001 From: Sidney Zhang Date: Mon, 29 Dec 2025 18:18:10 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=AC=A7=E6=8B=89=E9=A1=B9=E7=9B=AE=E7=AC=AC30=E9=A2=98?= =?UTF-8?q?=E5=92=8C=E7=AC=AC31=E9=A2=98=E7=9A=84=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=96=B9=E6=A1=88=20=F0=9F=93=9D=20docs=EF=BC=9A=E4=B8=BA?= =?UTF-8?q?=E7=AC=AC31=E9=A2=98=E6=B7=BB=E5=8A=A0=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E7=9A=84=E5=8A=A8=E6=80=81=E8=A7=84=E5=88=92=E7=AE=97=E6=B3=95?= =?UTF-8?q?=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- solutions/0030.FifthPowers/euler_30.py | 91 ++++++++++++++++++++ solutions/0031/euler_31.py | 27 ++++++ solutions/0031/readme.md | 114 +++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 solutions/0030.FifthPowers/euler_30.py create mode 100644 solutions/0031/euler_31.py create mode 100644 solutions/0031/readme.md diff --git a/solutions/0030.FifthPowers/euler_30.py b/solutions/0030.FifthPowers/euler_30.py new file mode 100644 index 0000000..d1a0162 --- /dev/null +++ b/solutions/0030.FifthPowers/euler_30.py @@ -0,0 +1,91 @@ +""" +Surprisingly there are only three numbers that can be written as the sum of fourth powers of their digits: + + 1634 = 1^4 + 6^4 + 3^4 + 4^4 + 8208 = 8^4 + 2^4 + 0^4 + 8^4 + 9474 = 9^4 + 4^4 + 7^4 + 4^4 + +As 1 = 1^4 is not a sum it is not included. + +The sum of these numbers is 19316. + +Find the sum of all the numbers that can be written as the sum of fifth powers of their digits. +""" + +import time +from itertools import combinations_with_replacement + + +def timer(func): + def wrapper(*args, **kwargs): + start = time.time() + result = func(*args, **kwargs) + end = time.time() + print(f"{func.__name__} on ({kwargs}) taken: {end - start: .6f} seconds") + return result + + return wrapper + + +def fifth_power(n: int, power: int = 5, strict: bool = False) -> tuple[bool, int]: + sumx = sum(int(digit) ** power for digit in str(n)) + if strict: + isok = (sumx == n) and (power == len(str(sumx))) + return isok, sumx + return sumx == n, sumx + + +def top_number(n: int) -> int: + beg = (9**n) * n + beg_len = len(str(beg)) + last = n + while True: + if beg_len >= n: + beg = (9**n) * beg_len + last = beg_len + beg_len = len(str(beg)) + if beg_len == last: + break + else: + break + return int("9" * beg_len) + + +def is_match(num: int, comb: list[str]) -> bool: + if num in [1, 0]: + return False + numstr = str(num).zfill(len(comb)) + numstr = sorted(numstr) + return numstr == sorted(comb) + + +@timer +def main_just_compute(power: int, strict: bool = False) -> None: + res = [] + top = top_number(power) + for i in range(2, top + 1): + is_sum, sumx = fifth_power(i, power, strict) + if is_sum: + print(sumx, end="\t") + res.append(sumx) + print(f"\nThe sum is {sum(res):,d}") + + +@timer +def main_combinations(power: int) -> None: + top_len = len(str(top_number(power))) + res = set() + combs = combinations_with_replacement("0123456789", top_len) + for comb in combs: + tmp = sum(map(lambda x: int(x) ** power, comb)) + if is_match(tmp, list(comb)): + print(tmp, end="\t") + res.add(tmp) + print(f"\nThe sum is {sum(res):,d}") + + +if __name__ == "__main__": + the_power = 5 + main_just_compute(power=the_power, strict=False) + print("=" * 50) + main_combinations(power=the_power) diff --git a/solutions/0031/euler_31.py b/solutions/0031/euler_31.py new file mode 100644 index 0000000..bee18e9 --- /dev/null +++ b/solutions/0031/euler_31.py @@ -0,0 +1,27 @@ +""" +In the United Kingdom the currency is made up of pound (£) and pence (p). +There are eight coins in general circulation: + + 1p, 2p, 5p, 10p, 20p, 50p, £1 (100p), and £2 (200p). + +It is possible to make £2 in the following way: + + 1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p + +How many different ways can £2 be made using any number of coins? +""" + + +def main(): + coins = [1, 2, 5, 10, 20, 50, 100, 200] + ways = [1] + [0] * 200 + + for coin in coins: + for i in range(coin, 201): + ways[i] += ways[i - coin] + + print(f"\nThe number of ways to make £2 is {ways[200]:,d}") + + +if __name__ == "__main__": + main() diff --git a/solutions/0031/readme.md b/solutions/0031/readme.md new file mode 100644 index 0000000..bb2fdc2 --- /dev/null +++ b/solutions/0031/readme.md @@ -0,0 +1,114 @@ +# 动态规划法 + +## 核心思路 + +这个函数采用**动态规划**的思想,通过构建一个 `ways` 数组来累积计算每个金额对应的方法数。 + +--- + +## 逐行解析 + +### 1. 初始化 +```python +coins = [1, 2, 5, 10, 20, 50, 100, 200] +ways = [1] + [0] * 200 # 结果是 [1, 0, 0, 0, ..., 0] (共201个元素) +``` + +- `coins`: 所有可用的硬币面值(单位:便士) +- `ways[i]`: 表示凑成金额 `i` 的方法数 +- **关键点**:`ways[0] = 1` 表示凑成0元有1种方法(什么都不用),这是动态规划的**基准条件** + +### 2. 双重循环的核心逻辑 +```python +for coin in coins: # 按顺序遍历每种硬币 + for i in range(coin, 201): # 从当前硬币面值遍历到200 + ways[i] += ways[i - coin] +``` + +这就是**状态转移方程**,其含义是: + +> **凑成金额 `i` 的方法数 = 原来方法数 + 使用当前硬币的方法数** + +其中 `ways[i - coin]` 表示:在使用了1枚当前硬币后,凑齐剩余金额的方法数。 + +--- + +## 具体执行过程演示 + +让我们跟踪计算前几个值的变化,以理解算法如何工作: + +### 初始状态 +``` +ways = [1, 0, 0, 0, 0, 0, ...] # ways[0]=1 +``` + +### 第1轮:使用硬币 1p +```python +for i in range(1, 201): + ways[i] += ways[i-1] +``` + +- `i=1`: `ways[1] += ways[0]` → `0 + 1 = 1` ✓ 凑1p: {1} +- `i=2`: `ways[2] += ways[1]` → `0 + 1 = 1` ✓ 凑2p: {1,1} +- `i=3`: `ways[3] += ways[2]` → `0 + 1 = 1` ✓ 凑3p: {1,1,1} +- ... +- **结果**:只用1p硬币,每个金额都有且仅有1种方法 + +### 第2轮:加入硬币 2p +```python +for i in range(2, 201): + ways[i] += ways[i-2] +``` + +关键更新点: +- `i=2`: `ways[2] += ways[0]` → `1 + 1 = 2` + - 原来:{1,1} + - 新增:{2} +- `i=3`: `ways[3] += ways[1]` → `1 + 1 = 2` + - 原来:{1,1,1} + - 新增:{1,2} +- `i=4`: `ways[4] += ways[2]` → `1 + 2 = 3` + - 原来:{1,1,1,1} + - 新增:{1,1,2}, {2,2} + +### 第3轮:加入硬币 5p +当加入5p硬币后,凑5p的方法从1种 {1,1,1,1,1} 变成2种,新增 {5}。 + +**以此类推**,直到处理完所有硬币类型。 + +--- + +## 为什么这样计算? + +这个算法的精妙之处在于: + +1. **按硬币顺序处理**:确保计算的是**组合数**(不考虑顺序) + - {1,2,2} 和 {2,1,2} 视为同一种方法 + - 如果调换循环顺序(先金额后硬币),会计算出排列数 + +2. **累积效应**:`ways` 数组保存的是**所有已处理硬币**能凑成的方法总数 + +3. **避免重复**:每种硬币只被考虑一次在其对应的外层循环中,确保不重复计算 + +--- + +## 最终结果 + +当循环结束后,`ways[200]` 的值就是 200(便士)能被凑成的所有方法数。 + +```python +print(f"\nThe number of ways to make £2 is {ways[200]:,d}") +``` + +输出结果是:**73682** + +这意味着用这8种英国硬币凑成2英镑共有**73,682**种不同的方式。 + +--- + +## 时间复杂度 + +- **O(N×M)**:其中 N = 硬币种类数(8),M = 目标金额(200) +- 实际计算量约为 8 × 200 = 1600 次操作,效率极高 + +这个算法简洁优雅,展示了动态规划在组合计数问题中的强大威力。