Skip to content

1.2 六问诊断法

学习目标

学完这一节,你能:

  • 在使用 AI 之前,写出自己的问题规格和初步判断
  • 用六问法把一个模糊任务拆成可检查的分析过程
  • 判断 AI 输出什么时候可以接受、什么时候必须追问或拒绝
  • 把六问法整理成一份"推理收据",留下自己的决策依据

试飞员的清单

2018 年,模拟训练。8000 米高空,600 公里时速,双引擎熄火。

试飞员没有先问"有没有最优方案"。他先跑过一张清单:

  1. 油量还剩多少?
  2. 哪个系统报错?
  3. 能重启吗?
  4. 最近机场在哪?
  5. 风向如何?
  6. 能滑过去吗?

清单不是成功的保证。清单的价值,是让人在压力下仍然有下一步动作。

算法设计也一样。面对一个陌生任务,尤其是一个可以立刻丢给 AI 的任务,你最危险的状态不是"不会写代码",而是还没有形成自己的判断,就已经继承了 AI 的判断。

六问诊断法就是算法设计中的清单。它不替你产出最优算法,而是帮助你在动手之前先建立一个可审查的思考基线。


六问诊断法的思考流程:从问题规格到责任边界,形成一个可回溯的分析循环
六问诊断法的思考流程:从问题规格到责任边界,形成一个可回溯的分析循环

先想再看:预测锁

使用六问法之前,先做一个更小的动作:预测锁

预测锁的要求很简单:碰 AI 之前,先写下你的立场,并封存时间。之后可以修正,但不能假装自己一开始就知道。

典型的错误路径是:

拿到问题 -> 立即问 AI -> AI 给出答案 -> 觉得有道理 -> 直接采纳

这不是协作,而是继承。你没有形成自己的判断,也就没有能力判断 AI 的答案解决的是不是原问题。

预测锁要求你先写下:

  • 我认为问题的输入和输出是什么
  • 我认为数据规模和速度要求大概是多少
  • 我想到的最简单解法是什么
  • 我猜测应该用哪类算法
  • 我最担心的边界情况是什么

然后再让 AI 参与。这样,AI 的输出就不再是"标准答案",而是一个可以和你原始判断对照的对象。

预测锁的目的不是证明你比 AI 强,而是暴露你没有想到的盲点。


预测锁流程:先写下独立判断,再引入 AI,最后比较差异并更新认知
预测锁流程:先写下独立判断,再引入 AI,最后比较差异并更新认知

为什么是六问?

六问不是随意凑出的六个问题。它们对应算法设计中的六个决策层次:

层次核心问题本层产出
理解问题我到底在解决什么?问题规格
建立基线最简单的可行办法是什么?暴力解和复杂度预算
检索知识这是不是已知问题?问题类型假设
利用结构有哪些特殊条件可以利用?可利用结构清单
选择范式应该采用哪种设计思想?范式选择和理由
承认边界还有哪些不确定或想不出来?风险列表和下一步行动

AI 可以参与每一层:它能帮你列可能性、查标准算法、写代码、找反例。但它不能替你承担问题定义和取舍责任。真实约束来自你的场景,最终责任也在你这里。

所以这一节训练的不是"不问 AI",而是训练你带着自己的判断去问 AI。


第一问:我真的理解问题吗?

第一问的产出不是代码,而是一张问题规格卡

规格卡至少包含五项:

项目要回答的问题常见遗漏
输入数据从哪里来?格式是什么?范围多大?把自然语言描述当成输入定义
输出返回一个值、一个列表,还是一个可解释的决策?只说"推荐结果",不说 Top-k 还是完整排序
规模n 大概是多少?峰值是多少?忽略线上高峰和长期增长
约束时间、空间、准确率、稳定性有什么要求?只关注能不能跑,不关注能不能上线
例子一个正常例子和一个边界例子分别是什么?没有冷启动、空输入、重复值等情况

比如"写个推荐算法"不是一个清晰问题。它至少要进一步变成:

输入是用户-商品评分矩阵,约 100 万用户、10 万商品;输出每个用户的 Top-10 推荐;在线请求需要 100ms 内返回;新用户允许退化到热门推荐。

这时才有资格讨论算法选择。

与 AI 协作

这一问最适合让 AI 做"澄清助手",而不是直接做"实现助手"。

可以这样问:

下面是我的问题规格。请帮我找出仍然模糊、可能导致算法选择错误的地方,不要先写代码。

如果 AI 立刻给代码,你也应该先把它拉回规格层面。规格不清,代码越完整越危险。


问题规格卡:输入、输出、规模、约束、例子共同决定后续算法选择
问题规格卡:输入、输出、规模、约束、例子共同决定后续算法选择

第二问:简单算法够用吗?

第二问的产出是一个暴力基线和一个复杂度预算

暴力解不是低水平解法。它有三个作用:

  • 作为正确性参照,帮助你测试更复杂的算法
  • 作为原型,帮助你确认问题建模是否正确
  • 作为预算起点,帮助你判断是否真的需要优化

关键不是问"暴力解是不是高级",而是问"它在当前规模下是否够用"。

一个粗略判断是:

数据规模O(n)O(n log n)O(n^2)O(2^n)
n = 100通常没问题通常没问题通常没问题通常不可接受
n = 10,000通常没问题通常没问题需要谨慎不可接受
n = 1,000,000通常没问题可能可接受通常不可接受不可接受

这张表不是硬规则。真实运行时间还取决于常数、语言、硬件、IO、网络、并发和数据分布。但它能防止你在没有规模意识时盲目接受一个复杂度不合适的方案。

与 AI 协作

不要只问:

帮我写最优算法。

更好的问法是:

先给出一个最简单正确的基线算法,并估计它在 n=10^5 时是否可接受。如果不可接受,再说明需要优化到什么复杂度。

这样 AI 的输出会更容易被审查:它必须同时给出解法和复杂度理由。


第三问:有标准解法吗?

第三问的产出是一个问题类型假设

很多任务表面上是业务需求,底层其实是经典问题。识别类型之后,你不需要重新发明算法,也不需要让 AI 在模糊描述中猜。

识别问题类型时,不要只背算法名字,要看信号:

识别信号可能的问题类型追问
有对象和关系点与连接构成的问题连接是否有方向和权重?
要找"最短、最快、最低成本"路线或代价最小化问题代价如何计算?
要安排、匹配、分配把资源分给对象的问题是一对一、多对一,还是容量受限?
要从序列中找规律排序、查找或分段问题是否只和相邻元素有关?
要在大量数据中判断是否出现过快速查找或去重问题是否允许概率性错误?
要实时处理连续数据只保留少量状态的数据流问题能否保存全部历史?

这一步的关键是:先提出假设,再验证假设。

例如"判断一个人能否参加所有会议"看起来是日程管理,其实可以假设为排序问题:把会议按开始时间排序,再检查相邻会议是否重叠。这个假设成立,是因为只要排序后相邻会议不冲突,就不会有跨越更远的冲突。

与 AI 协作

可以让 AI 同时给出多个类型假设:

这个问题可能分别被建模成哪些经典算法问题?请列出每种建模成立需要满足的条件,以及不成立时的反例。

这里你要审查的不是代码,而是"建模条件"。


问题类型识别:输入结构、输出目标和约束条件共同指向可能的标准解法
问题类型识别:输入结构、输出目标和约束条件共同指向可能的标准解法

第四问:能利用特殊结构吗?

第四问的产出是一个可利用结构清单

通用算法解决的是最一般的问题。但真实任务往往带有特殊条件:

  • 数据几乎有序
  • 数值范围很小
  • 图很稀疏
  • 查询很多但更新很少
  • 只需要近似答案
  • 可以离线预处理
  • 输入会持续到达,不能完整保存

这些条件可能彻底改变算法选择。

例如排序整数,如果数值范围只有 0 到 100,计数排序可能比通用比较排序更合适。又如推荐系统,如果离线阶段可以预计算候选集,在线阶段就不应该重新扫描全量商品。

第四问最容易被忽略,因为它要求你不仅理解算法,也理解数据来源和使用场景。

与 AI 协作

让 AI 写通用解之前,先让它读你的结构清单:

我的数据有这些特殊结构:……请判断哪些结构可以降低时间复杂度、空间复杂度或工程成本。每条建议都要说明适用条件。

如果 AI 提出的优化没有利用你的结构,它可能只是给了一个更复杂的通用解。


第五问:哪种范式适合?

第五问的产出是一个范式选择理由

算法范式不是标签,而是一组证明义务。选择某个范式,就意味着你要回答它最关键的问题:

范式先问什么如果答不上来,风险是什么
分治:把问题拆成相互独立的小问题子问题是否独立?合并是否便宜?子问题重叠导致重复计算
局部选择法:每一步都选眼前最好的动作局部最优为什么能推出全局最优?得到看似合理但错误的答案
记表法:把重复子问题的答案存起来状态是什么?转移是否覆盖所有情况?状态爆炸或遗漏边界
试探回退法:尝试一种选择,失败再退回搜索空间是什么?剪枝是否安全?枚举失控或剪掉正确答案
容量分配模型:把限制看作容量容量如何对应现实约束?建模错位,算法正确但问题错误
随机化:允许算法使用随机选择允许什么概率性错误?如何控制失败率?把不确定结果误当确定结果
数据流方法:输入持续到达,只保留少量状态能保存多少状态?是否允许近似?用单机批处理思路处理实时流

例如,看到"每一步选当前最优"并不能说明这个规则一定正确。你还要说明:为什么这个局部选择不会破坏未来的整体结果?如果说不清,就应该怀疑这个规则。

与 AI 协作

这里不要让 AI 只给算法名称。让它给出选择理由和反例压力测试:

你建议使用这个范式,请说明它成立需要满足的性质;再给一个最可能让该范式失败的反例。

如果 AI 只给出实现,不给出成立条件,你仍然没有完成算法设计。


范式选择决策树:每一种算法范式都对应一个必须回答的证明问题
范式选择决策树:每一种算法范式都对应一个必须回答的证明问题

第六问:还是想不出来?

第六问的产出是一个边界说明

想不出来不是失败。真正危险的是没有理由地说"做不了",或者没有证据地说"一定可以"。

更好的表达是:

我目前不能直接解决,因为……
我已经排除了……
我还不确定……
下一步我需要……

卡住时可以做四件事:

  • 回到第一问,检查问题是否定义错了
  • 放宽约束,先解决简化版本
  • 搜索相似问题,寻找标准建模
  • 请 AI 或专家给出反例、下界、替代建模

第六问把六问法变成循环。卡住不是终点,而是回到更早的问题重新建模。


六问法是循环而不是直线:卡住时回到问题规格、基线或建模假设重新检查
六问法是循环而不是直线:卡住时回到问题规格、基线或建模假设重新检查

推理收据:把六问法留下来

在 AI 时代,交付一个答案不够。你还要能交付答案背后的判断过程。

这份记录可以叫推理收据

text
问题规格:
- 输入:
- 输出:
- 规模:
- 约束:
- 例子:

暴力基线:
- 思路:
- 时间复杂度:
- 空间复杂度:
- 是否够用:

问题类型假设:
- 我认为它像:
- 成立条件:
- 可能反例:

特殊结构:
- 可利用结构:
- 对复杂度或工程成本的影响:

范式选择:
- 选择:
- 理由:
- 需要证明或测试的点:

AI 对比:
- AI 给出的方案:
- 与我预测不同之处:
- 我接受/修改/拒绝的理由:

剩余风险:
- 未确认的约束:
- 需要补充的测试:
- 下一步行动:

推理收据不是形式主义。它训练的是三种能力:

  • 你能不能在 AI 之前形成自己的判断
  • 你能不能解释为什么接受或拒绝 AI 的建议
  • 你能不能在结果出问题时追溯当初的决策依据

这正是未来算法课比过去要求更高的地方。过去学生常常被训练成"能手写实现的人";现在更需要被训练成"能定义问题、审查方案、承担判断的人"。


人与 AI 的分工

六问法不是把人和 AI 分开,而是把协作边界说清楚。

层次人的责任AI 可以协助
理解问题定义真实目标、约束和验收标准找歧义、补充澄清问题
建立基线判断复杂度是否够用写基线代码、估算复杂度
检索知识决定哪种建模最贴近场景列标准算法和类似问题
利用结构识别数据和业务特殊性提出可能优化方向
选择范式承担架构选择和证明义务给出实现、反例和测试
承认边界决定何时放宽、求助或停止提供替代方案和参考材料

AI 的价值很大,但它的价值取决于你给它什么问题,以及你如何审查它的回答。

所以,算法课不是因为 AI 会写代码而变得不重要。恰恰相反:当代码生成变便宜,判断错误变得更贵。


人与 AI 的分工:AI 扩展候选方案,人负责问题定义、约束取舍和最终审查
人与 AI 的分工:AI 扩展候选方案,人负责问题定义、约束取舍和最终审查

综合示例:用六问法分析"判断图是否有环"

前面我们分别介绍了六问的每一问。现在,让我们用一个完整问题——判断无向图是否存在环——来演示六问法如何贯穿整个分析过程。

先写预测锁

假设你接到这个任务:

给定一个无向图,判断其中是否存在环。

在碰 AI 之前,先写下你的预测:

text
我的预测锁:
- 输入:一组点和边,点编号 1 到 n,边是点的配对
- 输出:true(有环)或 false(无环)
- 规模猜测:点数 n 约 10^5,边数约 2×10^5
- 最简单解法:对每个点,尝试走一条路径,看能否回到自己
- 复杂度猜测:O(n²) 或更坏,因为可能重复访问很多路径
- 我担心:怎么避免重复计算?怎么判断"回到自己"是环还是正常折返?

这个预测可能不准确。没关系,预测锁的目的正是暴露盲点。


第一问:问题规格卡

现在把模糊需求变成清晰规格:

text
问题规格卡:
- 输入:
  - n 个点,编号 1 到 n
  - m 条无向边,每条边是两个点的配对 (u, v)
  - 图可能不连通(分成多个独立部分)
- 输出:
  - 如果图中存在环,返回 true
  - 否则返回 false
- 规模:
  - n ≤ 10^5,m ≤ 2×10^5
- 约束:
  - 时间 < 1s
  - 空间 < 256MB
- 例子:
  - 正常例子:3 个点,边 (1,2), (2,3), (3,1) → 有环,返回 true
  - 边界例子:0 个点,0 条边 → 无环,返回 false
  - 边界例子:1 个点,0 条边 → 无环,返回 false

到这里,你已经能判断:数据规模较大,O(n²) 可能不够。


第二问:暴力基线和复杂度预算

最简单的解法是什么?

暴力思路:对每个点,尝试深度搜索所有路径,看能否回到已经访问过的点(除了直接父节点)。

text
暴力基线:
- 思路:DFS 遍历,如果发现一个已经访问过的点(非父节点),就有环
- 时间复杂度:O(n + m),每个点和边只访问一次
- 空间复杂度:O(n),记录访问状态
- 是否够用:对于 n = 10^5,O(n + m) 约 3×10^5 次操作,通常可接受

重要发现:我最初的预测(O(n²))是错的!仔细思考后发现,DFS 只需要每个点和边访问一次。

这就是预测锁的价值:你先写一个可能错误的猜测,再通过思考或 AI 协作纠正它。


第三问:问题类型假设

这个问题属于什么类型?

text
问题类型假设:
- 我认为它像:图的连通性问题
- 典型特征:需要遍历所有点和边,判断某种结构是否存在
- 已知解法:DFS/BFS 都可用于环检测
- 成立条件:
  - 无向图,每条边等价于双向连接
  - 环的定义:一条边连向已访问过的点(非父节点)
- 可能反例:
  - 如果图有自环(点连向自己),算不算环?需要规格明确
  - 如果有重边(两点之间多条边),算不算环?需要规格明确

与 AI 协作示例

这个问题可能有哪些建模方式?请列出每种建模成立需要满足的条件,以及不成立时的反例。

AI 可能回答:

  • DFS 检测:访问到已访问点(非父节点)即为环。条件:无向图。反例:如果允许自环,DFS 需要单独判断。
  • Union-Find 检测:每次合并两点所属集合,如果两点已在同一集合,则有环。条件:边逐一加入。反例:如果边有方向,Union-Find 不适用。

第四问:可利用结构清单

有没有特殊条件可以利用?

text
可利用结构:
- 图可能不连通 → 需要对每个连通部分单独检查
- 边数远小于 n²(稀疏图) → DFS/BFS 更适合,不需要邻接矩阵
- 只需要判断"有没有环",不需要找出所有环 → 可以提前终止
- 边可以按任意顺序处理 → Union-Find 可用

对复杂度的影响:
- 稀疏图用邻接表,空间 O(n + m) 而非 O(n²)
- 提前终止:一旦发现环就可以返回,不需要遍历全图

第五问:范式选择理由

应该用什么范式?

方案 A:DFS + 访问标记

text
范式选择:DFS(试探回退法的变体)
- 思路:从一个点出发,深度探索;遇到已访问点(非父节点)则发现环
- 成立条件:需要区分"正常折返"和"真正的环"
- 关键问题:如何记录父节点?
- 需要证明的点:为什么遇到已访问点(非父节点)必然意味着环?

证明草图

假设从点 u 出发 DFS,当前在点 v,发现邻居 w 已被访问且 w ≠ parent[v]。

这意味着有一条路径 u → ... → v,同时有一条路径 u → ... → w。

现在 v 和 w 之间有一条边。两条路径加上这条边,形成了一个环。

方案 B:Union-Find

text
范式选择:Union-Find(集合合并)
- 思路:逐一处理边,每次合并两点所属集合;如果两点已在同一集合,说明有环
- 成立条件:无向图,边逐一加入
- 关键问题:为什么"两点已在同一集合"意味着环?
- 需要证明的点:Union-Find 不直接给出环的路径,只判断是否存在

证明草图

如果两点 u 和 v 已在同一集合,说明之前有路径连通它们。

现在又有一条边 (u, v),这条边加上之前的路径形成环。


第六问:边界说明和不确定点

text
我目前不确定:
- 自环算不算环?规格没有明确
- 重边算不算环?规格没有明确
- 如果图很大,DFS 的栈深度会不会溢出?

下一步:
- 回到第一问,补充边界约定:自环算环,重边算环
- 如果栈深度有风险,改用迭代式 DFS 或 Union-Find
- 用 AI 生成测试用例,覆盖自环、重边、空图等边界

最终推理收据

把六问分析整理成一份完整的收据:

text
问题规格:
- 输入:n 点,m 边(无向),点编号 1..n
- 输出:true/false
- 规模:n ≤ 10^5, m ≤ 2×10^5
- 约束:< 1s, < 256MB
- 例子:(1,2),(2,3),(3,1) → true;空图 → false

暴力基线:
- 思路:DFS,遇到已访问点(非父节点)即有环
- 时间:O(n + m)
- 空间:O(n)
- 是否够用:够用

问题类型假设:
- 类型:图连通性问题
- 成立条件:无向图
- 反例压力:自环、重边需单独约定

特殊结构:
- 稀疏图 → 用邻接表
- 不连通 → 对每部分单独 DFS
- 提前终止 → 发现环立刻返回

范式选择:
- 方案 A:DFS + 父节点记录
- 方案 B:Union-Find
- 选择理由:两者都是 O(n + m);DFS 更直观,Union-Find 更简洁

AI 对比:
- AI 建议:DFS 或 Union-Find
- 与我预测差异:我最初猜 O(n²),修正后发现 O(n + m)
- 接受理由:预测锁暴露盲点,六问分析验证

剩余风险:
- 边界约定需补充:自环、重边
- 栈深度风险:大数据时考虑迭代 DFS
- 下一步:构造测试用例覆盖边界

关键洞察

通过这个综合示例,你可以看到六问法的真正价值:

  1. 预测锁暴露盲点:我最初猜 O(n²),通过第二问的分析发现其实 O(n + m)。
  2. 规格卡防止歧义:如果没有第一问,可能遗漏"自环算不算环"的边界约定。
  3. 类型假设缩小搜索范围:识别出"图的连通性问题",直接锁定 DFS/BFS/Union-Find。
  4. 结构清单优化方案:稀疏图用邻接表,提前终止减少不必要的遍历。
  5. 范式选择需要证明:不是只说"用 DFS",而是要说明为什么遇到已访问点意味着环。
  6. 边界说明驱动下一步:不确定的地方写成"下一步",而不是假装不存在。

六问法不是让你"一次全想清楚"。它是让你在想不清楚的地方有明确的下一步动作


本节要点

  • 六问法不是求最优解的公式,而是防止思考失控的纪律
  • 预测锁要求你在 AI 之前形成自己的判断基线
  • 每一问都要有产出:规格、基线、类型假设、结构清单、范式理由、边界说明
  • AI 可以参与分析,但不能替你承担问题定义和最终取舍
  • 高质量算法学习不再只是写出代码,而是留下可审查的推理收据
  • 综合示例展示了六问法如何贯穿一个完整问题的分析过程

课堂练习

练习 1:预测锁训练

问题:给定一个无向图,判断其中是否存在环。

第一步,不使用 AI,写一份预测锁:

  1. 你如何定义输入和输出?
  2. 你想到的最简单解法是什么?
  3. 你预测的时间复杂度和空间复杂度是多少?
  4. 你认为这个问题像哪类经典问题?
  5. 你担心哪些边界情况?

第二步,让 AI 给出两种不同解法,并要求它说明复杂度和边界情况。

第三步,提交一张对比表:

项目我的预测AI 输出我最终接受的判断
问题建模
算法思路
复杂度
边界情况

评分重点不是谁先想到标准解,而是谁能清楚说明自己为什么修改判断。


练习 2:六问法应用

用六问法分析下面的问题:

给定 n 个会议的起始和结束时间,判断一个人能否参加所有会议。

你的提交必须包含:

  • 问题规格卡
  • 暴力基线和复杂度
  • 标准解法假设
  • 至少两个边界例子
  • 最终给 AI 的提示词
  • 你会用哪些测试判断 AI 的实现是否正确

练习 3:审查 AI 输出

问题仍然是会议安排。假设 AI 给出如下判断:

先按会议开始时间排序。然后检查每个会议的开始时间是否大于前一个会议的结束时间。如果都大于,就可以参加所有会议。

请审查这段答案:

  1. 它的算法思想是否合理?
  2. "大于"是否一定正确?如果一个会议 10:00 结束,下一个会议 10:00 开始,算不算冲突?
  3. 这个答案缺少哪些输入约定?
  4. 你会如何修改提示词,让 AI 先问清楚边界再写代码?

练习 4:推理收据

任选一个你最近见过的编程任务,按本节模板写一份推理收据。要求:

  • 先写自己的预测锁
  • 再附上 AI 输出摘要
  • 最后写出你接受、修改或拒绝 AI 建议的理由

课后测验

📝 课后测验

得分:0 / 0

上一节:1.1 你面对的是一个什么问题

下一节:1.3 如何验证正确性

新时代的算法课程