原题地址:https://pe-cn.github.io/932/

一开始让他自由发挥,结果写出来的程序是错的。
然后让他从穷举法开始编写,
python
# coding=utf-8
def find_all_2025_numbers():
results = []
# 枚举所有可能的 a 和 b
for a in range(1, 5000):
for b in range(1, 5000):
# 计算连接后的数
# 先确定 b 的位数
b_digits = len(str(b))
N = a * (10 ** b_digits) + b
# 检查是否为完全平方数
s = a + b
if N == s * s:
results.append((N, a, b))
# 按 N 排序并去重
results = sorted(set(results), key=lambda x: x[0])
# 输出结果
print(f"{'N':<10} {'a':<10} {'b':<10} {'a+b':<10} (a+b)²")
print("-" * 50)
for N, a, b in results:
s = a + b
print(f"{N:<10} {a:<10} {b:<10} {s:<10} {s*s}")
print(f"\n共找到 {len(results)} 个 2025 数")
return results
if __name__ == "__main__":
find_all_2025_numbers()
把输出反馈给他理解。
n<=5000有如下规律:请分析总结
N a b a+b (a+b)²
--------------------------------------------------
81 8 1 9 81
2025 20 25 45 2025
3025 30 25 55 3025
88209 88 209 297 88209
494209 494 209 703 494209
4941729 494 1729 2223 4941729
7441984 744 1984 2728 7441984
24502500 2450 2500 4950 24502500
25502500 2550 2500 5050 25502500
上述穷举程序效率低,根据完全平方数的后几位找b,前几位找a的思路来重写,不做别的
终于编对了, 改进版如下:
根据完全平方数的性质,我来重写高效的程序:
python
def solve_T16():
n = 16
total_sum = 0
seen = set()
# 枚举可能的位数 k (b的位数)
for k in range(1, n):
# 10^k 和 10^k - 1
pow10_k = 10 ** k
pow10_k_minus_1 = pow10_k - 1
# b 的范围:k 位数
min_b = 10 ** (k - 1)
max_b = pow10_k - 1
# 对于每个可能的 b,我们需要解方程:
# N = s^2,且 N 的最后 k 位是 b
# 即 s^2 ≡ b (mod 10^k)
# 枚举可能的 s 模 10^k 的值
# 实际上,我们需要 s^2 ≡ b (mod 10^k)
# 可以通过枚举 s 模 10^k 来找到所有解
# 对于给定的 b,我们需要找到所有 s 使得 s^2 ≡ b (mod 10^k)
# 然后检查对应的 N 是否 ≤ 10^n
# 更高效的方法:直接枚举 s 并检查 b 的位数条件
# s 的范围:s^2 的位数 ≤ n,所以 s ≤ 10^(n/2)
max_s = int(10 ** (n / 2)) + 1
# 对于每个可能的 s 模 10^k 的余数,我们可以跳着枚举
# 但更简单:枚举所有 s 并检查,因为 n=16 时 s 只有 10^8 个,但这对 Python 来说太大了
# 改用数学方法:b = s(10^k - s)/(10^k - 1) 必须为整数
# 所以 (10^k - 1) | s(10^k - s)
# 枚举 s 模 10^k - 1 的倍数
mod_val = pow10_k_minus_1
max_s_val = max_s
# 从最小的可能 s 开始
s_start = 2 # s = a+b ≥ 2
# 对于每个可能的 s,检查 b 是否为整数且为 k 位数
for s in range(s_start, max_s_val + 1):
# 计算 b = s(10^k - s)/(10^k - 1)
numerator = s * (pow10_k - s)
if numerator % pow10_k_minus_1 != 0:
continue
b = numerator // pow10_k_minus_1
# 检查 b 是否为 k 位数
if b < min_b or b > max_b:
continue
# 计算 a = s - b
a = s - b
if a <= 0:
continue
# 验证 N = a * 10^k + b = s^2
N = a * pow10_k + b
if N != s * s:
continue
# 检查 N 的位数是否 ≤ n
if len(str(N)) > n:
continue
if N not in seen:
seen.add(N)
total_sum += N
# print(f"N={N}, a={a}, b={b}, k={k}, s={s}")
return total_sum
if __name__ == "__main__":
result = solve_T16()
print(f"T(16) = {result}")
这个程序的思路:
- 固定 b 的位数 k
- 直接枚举 s = a+b,因为 s 的范围相对较小(s ≤ 10^(n/2) = 10^8)
- 通过公式 b = s(10^k - s)/(10^k - 1) 计算 b
- 检查 b 是否为整数且为 k 位数
- 验证所有条件并累加结果
对于 n=16,s 最多有 10^8 个,但每个 k 都要遍历一次,总计算量约为 16 × 10^8 = 1.6e9,在 Python 中仍然较大,但比枚举 a,b 要高效很多。
实际运行速度有点慢。n=16,用了4分多钟。再让他优化。
你说得对,当 n=16 时,s 需要遍历到 10^8,这个数量级在 Python 中仍然太慢。我需要进一步优化。
关键优化点:利用 s(10^k - s) 必须被 10^k - 1 整除的条件,可以大幅减少枚举量。
python
def solve_T16():
n = 16
total_sum = 0
seen = set()
# 枚举可能的位数 k (b的位数)
for k in range(1, n):
pow10_k = 10 ** k
d = pow10_k - 1 # 10^k - 1
# b 的范围:k 位数
min_b = 10 ** (k - 1)
max_b = pow10_k - 1
# s 的最大值:s^2 的位数 ≤ n,所以 s ≤ 10^(n/2)
max_s = int(10 ** (n / 2))
# 我们需要 s(10^k - s) ≡ 0 (mod d)
# 即 s(10^k - s) = s(pow10_k - s) ≡ 0 (mod d)
# 由于 pow10_k ≡ 1 (mod d),所以 s(1 - s) ≡ 0 (mod d)
# 即 s(s - 1) ≡ 0 (mod d)
# 所以 d 必须整除 s(s-1)
# 这意味着 s 要么是 d 的倍数,要么 s-1 是 d 的倍数
# 或者更一般地,s 和 s-1 分别包含 d 的因子
# 方法1:枚举 d 的倍数
# s 必须是 d 的倍数,或者 s-1 是 d 的倍数
# 但更精确地,我们需要考虑 d 的因子分解
# 简单但有效的方法:枚举 d 的所有因子对
# 对于每个因子 f 整除 d,设 s 是 f 的倍数,且 s-1 是 d/f 的倍数
# 获取 d 的所有因子
factors = []
i = 1
while i * i <= d:
if d % i == 0:
factors.append(i)
if i != d // i:
factors.append(d // i)
i += 1
# 对于每对因子 (f, g) 满足 f * g = d
# 我们需要 s 满足:f | s 且 g | (s-1)
# 即 s ≡ 0 (mod f) 且 s ≡ 1 (mod g)
# 使用中国剩余定理求解
for f in factors:
g = d // f
# 解同余方程组:s ≡ 0 (mod f), s ≡ 1 (mod g)
# 需要 gcd(f, g) = 1,否则可能无解
from math import gcd
if gcd(f, g) != 1:
continue
# 使用扩展欧几里得算法求解
# 找到 x, y 使得 f*x + g*y = 1
# 则 s = f * (1 - 0) * y? 实际上标准解是:
# s = 0 * g * inv_g_mod_f + 1 * f * inv_f_mod_g
# 但更简单:s = f * ((1 * inv_f_mod_g) % g)
# 计算 f 模 g 的逆元
inv_f_mod_g = pow(f, -1, g)
s0 = f * inv_f_mod_g % (f * g)
s0 = s0 % d
# s0 是模 d 的一个解,所有解为 s = s0 + t*d
# 枚举所有 s ≤ max_s
if s0 == 0:
s0 = d # 避免 s=0
s = s0
while s <= max_s:
# 检查这个 s 是否有效
if s >= 2: # s = a+b ≥ 2
# 计算 b = s(10^k - s)/d
numerator = s * (pow10_k - s)
if numerator % d == 0:
b = numerator // d
# 检查 b 是否为 k 位数
if min_b <= b <= max_b:
a = s - b
if a > 0:
N = a * pow10_k + b
if len(str(N)) <= n and N not in seen:
seen.add(N)
total_sum += N
s += d
# 同样处理 s ≡ 1 (mod f), s ≡ 0 (mod g) 的情况
# 交换 f 和 g 的角色
inv_g_mod_f = pow(g, -1, f)
s1 = g * inv_g_mod_f % (f * g)
s1 = s1 % d
if s1 == 0:
s1 = d
s = s1
while s <= max_s:
if s >= 2:
numerator = s * (pow10_k - s)
if numerator % d == 0:
b = numerator // d
if min_b <= b <= max_b:
a = s - b
if a > 0:
N = a * pow10_k + b
if len(str(N)) <= n and N not in seen:
seen.add(N)
total_sum += N
s += d
return total_sum
if __name__ == "__main__":
result = solve_T16()
print(f"T(16) = {result}")
这个优化的关键点:
-
利用同余关系 :从
s(10^k - s) ≡ 0 (mod d)且10^k ≡ 1 (mod d)推导出s(s-1) ≡ 0 (mod d) -
因子分解:d 的每个因子对 (f, g) 满足 f × g = d,对应两种情况:
- f | s 且 g | (s-1)
- g | s 且 f | (s-1)
-
中国剩余定理:解同余方程组得到 s 模 d 的解,然后每次增加 d 枚举所有可能的 s
这样枚举量从 O(10^8) 降低到 O(因子个数 × (max_s / d)),对于大多数 k,d 很大,枚举量很小。
这个版本n=16只用18秒就算完了。