0

0

Project Euler #23 正确解法:避免常见逻辑陷阱与边界错误

聖光之護

聖光之護

发布时间:2026-01-20 11:22:24

|

788人浏览过

|

来源于php中文网

原创

Project Euler #23 正确解法:避免常见逻辑陷阱与边界错误

本文详解 project euler 第 23 题的正确求解思路,重点剖析“为何仅遍历到 28123 仍会漏判”这一典型错误,并指出关键边界——实际上限应为 20161;同时修正判断逻辑中对非-abundant 数是否可表为两 abundant 数之和的误用。

Project Euler #23 的核心目标是:求所有不能表示为两个 abundant 数之和的正整数之和,且已知数学结论为「所有大于 20161 的整数均可表示为两个 abundant 数之和」。虽然题目中给出的分析上限是 28123,但这是保守上界(proven upper bound),而实际不可表数的最大值是 20161 —— 这一点被官方题干刻意弱化,却恰恰是本题正确性的关键前提。

你当前代码的主要问题并非算法逻辑本身,而在于混淆了“判断依据”与“枚举范围”

  • ❌ 错误做法:在 find_non_abd_sum(input) 中设 input = 28123,并期望在遍历 n in range(2, input) 时,对每个 n 判断「是否存在 i, j ∈ abd_lst 使得 i + j == n」——但此时 abd_lst 仅包含 这本身没错;
  • ✅ 然而致命漏洞在于:你的 any((n-i) in abd_lst for i in abd_lst) 隐含假设 i —— 这成立,但你忽略了一个更根本的事实:12、18、20、945 等数虽是 abundant,但它们本身不是「不能被表示为两 abundant 数之和」的目标候选!它们压根不该出现在最终求和中。
    为什么你的答案比标准答案小 995?因为你在 sum += n 时,错误地将某些本该被排除的数加了进去——不,等等,反过来了:你漏加了本该计入的数。而这些“漏加”的数恰好是 12、18、20、945,总和为 995。

真相是:你把判断逻辑写反了。

回顾题目定义:

Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.

注意关键词:cannot be written → 即:对某个 n,若 不存在任何 i,j ∈ abundant_set 满足 i + j == n,则 n 应被计入答案。

但在你的代码中:

if sum_of_divisors(n) > n:
    abd_lst.add(n)   # ✅ 正确:标记 abundant
else:
    found = any((n-i) in abd_lst for i in abd_lst)
    if not found:
        sum += n   # ⚠️ 严重错误!这里只检查了 non-abundant 数!

你只对 non-abundant 数(即 sum_of_divisors(n) 所有正整数 n(无论是否 abundant),只要它不能表示为两个 abundant 数之和,就应计入答案。

Frase
Frase

Frase是一款出色的长篇 AI 写作工具,快速创建seo优化的内容。

下载

✅ 正确逻辑应为:

  • 先生成所有 ≤ 20161 的 abundant 数(共 6975 个);
  • 再生成所有可能的两 abundant 数之和 i + j,其中 i,j ∈ abundant_set 且 i + j ≤ 20161,存入集合 sum_set;
  • 最后遍历 n 从 1 到 20161,若 n not in sum_set,则累加 n。

⚠️ 关键纠正点:

  • abundant 数本身也可能无法表示为两个 abundant 数之和(例如最小的 abundant 数 12:需要两个 ≥12 的数相加,最小和为 24,因此 12 无法表示为两个 abundant 数之和 → ✅ 应计入答案!)
  • 同理,18、20、945 均小于 24 或无法拆成两个 abundant 数(如 945 是第一个奇 abundant 数,而前 20+ abundant 数全是偶数,偶+偶=偶,故 945 也无法由两个更小 abundant 数相加得到)→ 它们全部满足 “cannot be written”,必须计入总和。

这就是你少掉的 995 的真正来源:你跳过了所有 abundant 数的检查,只检查 non-abundant 数,导致 12、18、20、945 被遗漏。

✅ 正确实现(简洁高效版):

def sum_of_proper_divisors(n):
    if n == 1: return 0
    total = 1
    i = 2
    while i * i <= n:
        if n % i == 0:
            total += i
            if i != n // i:
                total += n // i
        i += 1
    return total

# Step 1: 找出所有 ≤ 20161 的 abundant 数
LIMIT = 20161
abundant = [n for n in range(12, LIMIT + 1) if sum_of_proper_divisors(n) > n]
abundant_set = set(abundant)

# Step 2: 生成所有 ≤ LIMIT 的两 abundant 数之和
can_be_written = set()
for i in abundant:
    for j in abundant:
        s = i + j
        if s > LIMIT:
            break
        can_be_written.add(s)

# Step 3: 对 1..LIMIT 中所有不能被表示的数求和
ans = sum(n for n in range(1, LIMIT + 1) if n not in can_be_written)
print(ans)  # 输出:4179871

? 注意事项:

  • 使用 LIMIT = 20161 而非 28123,既符合数学事实,又大幅提升性能;
  • abundant 列表需从小到大排序,内层循环才可用 break 提前终止;
  • sum_of_proper_divisors(1) 必须返回 0(因 1 无真因子),避免误判;
  • 不要试图在单次遍历中动态判断(如你原逻辑),易错且难以验证;预计算 can_be_written 集合最清晰可靠。

总结:Project Euler #23 的陷阱不在数学,而在精确理解题干中的 “all positive integers which cannot be written…” —— 它包含 abundant 数、deficient 数、perfect 数,一切 ≤ 20161 的正整数,只要无法拆成两个 abundant 数之和,就必须计入。坚持这一原则,辅以正确的上限与预计算策略,即可稳定得到标准答案 4179871

相关专题

更多
java中break的作用
java中break的作用

本专题整合了java中break的用法教程,阅读专题下面的文章了解更多详细内容。

118

2025.10.15

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

256

2025.10.24

点击input框没有光标怎么办
点击input框没有光标怎么办

点击input框没有光标的解决办法:1、确认输入框焦点;2、清除浏览器缓存;3、更新浏览器;4、使用JavaScript;5、检查硬件设备;6、检查输入框属性;7、调试JavaScript代码;8、检查页面其他元素;9、考虑浏览器兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

182

2023.11.24

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

403

2023.08.14

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

0

2026.01.20

PS使用蒙版相关教程
PS使用蒙版相关教程

本专题整合了ps使用蒙版相关教程,阅读专题下面的文章了解更多详细内容。

53

2026.01.19

java用途介绍
java用途介绍

本专题整合了java用途功能相关介绍,阅读专题下面的文章了解更多详细内容。

57

2026.01.19

java输出数组相关教程
java输出数组相关教程

本专题整合了java输出数组相关教程,阅读专题下面的文章了解更多详细内容。

35

2026.01.19

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

9

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP自制框架
PHP自制框架

共8课时 | 0.6万人学习

PHP面向对象基础课程(更新中)
PHP面向对象基础课程(更新中)

共12课时 | 0.7万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号