猜一个数:让搜索朝终点走
全课题骨架:寻路不是"找一条路",而是"给地图上每个位置记一个数",而记数只有一条规则 ——
从已经确定的那一圈往外推一格,给新格记下它自己的数。
上一课的缺口很直白:泛洪朝四面八方推,而终点只在一个方向 —— 它把 965 个格全定了数,
其中绝大多数与最优路径毫无关系。这一课只补一件事:让每一格的数里,再加一项"从它到终点还要花多少"的猜测,
取的时候按"已经花的 + 猜的还要花的"来取 —— 搜索就朝终点那边偏过去了。
四段路:先把这一项加上(25.1)→ 问"猜多少才合适"(25.2)→ 问"猜过头会怎样"(25.3)→
干脆把它当旋钮用,并给出一个上界(25.4)。
★ 量出来才敢写的一件事:加了这个猜测之后,要定的格从 965 降到 233(少算 75.9%),而解一模一样
—— 代价都是 47;而且有一条完全独立的路线把这个 47 又算了一遍(从终点反向泛洪,得到
h*(起点) = 47)。
另一件同样重要:猜测越过"不高估"这条线之后,答案就真的会坏 —— 实测 k = 1.25 那档虽然已经有
841 个格被高估,却仍然给出最优解;一直到 k = 1.5 才坏(代价 52,比最优贵 5)。
也就是说"不可采纳"丢掉的不是"答案",而是"保证"。
保真度:示意 · 等价重现 —— A*、反向泛洪、参考解都是纯图算法
(页面现场跑的十条与离线定稿 q112.mjs 同口径),画面是本页自己的二维软件光栅,
不是任何引擎或导航库的内部实现。
★ 本页与第 1 课同一张地图、同一套价(泥地 ×4)—— 所以 965 / 47 这些数可以两页对照着看。
★ 本页不做对角走位、不做 JPS(那是第 3 课);平局怎么断本页钉死成"下标最小的那个"
(一手源原话:"Resolve ties arbitrarily, but always in favor of any node n ∈ T"),换断法会改工作量、不改代价。
本页的原理与四张数值表照常可读(画面只是把同一张搜索表涂出来)。
(脚本启动中…)
定点观察:(脚本启动中…)
本帧的读数
(脚本启动中…)
25.1 段 · 给数加一项"猜"
本段只增加一样东西:那个猜测 h(读者只动「单步 / 跑到底」,段内 k 固定在 1、W 固定在 1)。
保真度:示意 · 等价重现。上一课取的是"d 最小的待定格",也就是只按已经花了多少排队;
本段把队列的键换成 f = g + h:g 是"已经花了多少",
h 是"从这一格到终点还要花多少"的猜测。取最小的仍然是同一句话,只是那个"数"里多了一项。
★ 一手源把这两件事写成一句(HN R 1968 原话):"We can write f(n) as the sum of two parts: f(n)=g(n)+h(n)",
以及取的时候 "Select the open node n whose value of f is smallest."
★ 本段的猜测取最朴素的那个:h = 曼哈顿距离(横格数 + 竖格数)。
它为什么"不算高估"?因为每走一步至少要花 1(本页最便宜的格也是 1)——
所以"还要走几步"永远不会超过"还要花多少"。
★ 实测:泛洪定 965 格,A* 只定 233 格(少算 732 格,75.9%),
而两者的解代价都是 47。另有第三条独立路线给这个 47 作证:从终点反向泛洪得到的
h*(起点) = 47,与它逐位相等。
f = g + h),
中间是本页真跑的 st.step(逐字),右列是实测。
高亮那一行就是本课的全部改动:f[m] = cand + W * H[m]; ——
上一课是 d[m] = cand;,差别只在排队用哪个数。| 记的账 | 应然 | 实测 | 结论 | 怎么算 | 类别 |
|---|
定点观察:① 按「跑到底」(再按「单步」可以从头看过程); ② 看画面里深色那一块(A* 定的格)与最淡那一层(第 1 课泛洪定的格)的形状差别; ③ 结论:加了"猜"之后,搜索只朝终点那一侧铺开,定的格从 965 降到 233,而解一位不差。
25.2 段 · 猜多少
本段只增加一样东西:一个滑块 k(h = k × 曼哈顿,段内 k 只能在 0 与 1 之间动)。
保真度:示意 · 等价重现。上一段的猜测是"每步至少花 1"这个最保守的估法;可你要是猜得比它小呢?
k = 0 就是"完全不猜"(退回上一课),k = 1 是上一段那一档,中间是各种"猜一点"。
本段开页就在 k = 0 那一端(你会看到画面与上一课的泛洪一模一样),从那儿往右拖即可。
★ 这一段真正要看的是一件反直觉的事:拖这个滑块,解代价一格都不动(全是 47),
动的只是"要定多少格" —— 774 → 696 → 626 → 536 → 233。
也就是说"猜得准"买到的不是"答案更好",而是"算得更少"。
★ 为什么一整段都安全?因为这一整段的 k 都落在同一个区间里:h 从不高估 h*("可采纳")。
本段还顺手把这件事数出来了:逐格检查 h > h* 的格数,五档全部是 0。
★ 顺带一条自洽:k = 0 那一档的"已定 774 格"必须等于上一课那条早退泛洪的读数 —— 实测两档都是
774,代价也都是 47。
| 记的账 | 应然 | 实测 | 结论 | 怎么算 | 类别 |
|---|
定点观察:① 拖「猜多少 k」那个滑块(从 0 拖到 1); ② 看画面里深色那一块怎么从一大片收成一条朝终点的带,同时盯住读数里的"已定"与"解代价"; ③ 结论:解代价一格都不动(全是 47),动的只是"要定多少格" —— 猜得准买到的不是"答案更好",而是"算得更少"。
25.3 段 · 猜过头
本段只增加一样东西:滑块的量程被放开到 1 以上(越过"不高估"那条线;其余控件与上一段完全相同)。
保真度:示意 · 等价重现。上一段那一整段安全,是因为 k 一直被压在 1 以内。本段把这条线跨过去 ——
猜测开始高估,而高估正是最优性证明里唯一不能碰的东西。
★ 结果比"会坏"更值得记:它不一定坏。k = 1.25 那一档已经有 841 个格被高估("不可采纳"已经成立),
可它仍然给出最优解 47;一直到 k = 1.5 才真的坏:代价 52,比最优贵 5。
★ 所以"可采纳"丢掉的不是"答案",而是"保证":越过那条线之后,
算法可能给出次优解 —— 具体坏不坏、坏在哪一档,只能量。
★ 一手源把"不高估"写成一句(HN R 1972 勘误原话):
"if the heuristic function ĥ(n) is a lower bound on the true minimal cost from node n to a goal node,
then A* is admissible; i.e., it would find a minimal cost path if any path to a goal node existed."
★ 本段的读数多了一格:"高估的格数" —— 逐格比较 h 与 h*(h* 由"从终点反向泛洪"独立算出)。
它从 0 变成 841 / 941 / 964,而解代价在 1.25 那一档还不动。这两个数并排看,才是这一段的全部内容。
| 记的账 | 应然 | 实测 | 结论 | 怎么算 | 类别 |
|---|
定点观察:① 把「猜多少 k」拖过 1(一直拖到 3); ② 看读数里"高估的格数"与"解代价"这两列—— 前者立刻不再是 0,后者要过一会儿才动; ③ 结论:越过"不高估"这条线丢掉的不是答案,而是保证 —— k = 1.25 已经有 841 个格被高估,却仍然给出最优解;到 k = 1.5 才真的坏(贵 5)。
25.4 段 · 把它当旋钮用
本段只增加一样东西:权重 W(f = g + W·h,W ≥ 1;上一段那个 k 滑块不动)。
保真度:示意 · 等价重现。上一段把"猜过头"当成一种错误;本段换个身份看它:
它是一个旋钮 —— 你故意多信一点猜测,换更少的计算量,代价是解可能不是最优的
但不至于离最优多远:C ≤ W · C*。
★ 一手源原话(Pohl 1970 的加权 A\*,转引自 Chen / Sturtevant / Doyle / Ruml《Revisiting Suboptimal Search》2019):
"WA* uses a priority function of f(n) = g(n) + w · h(n) and returns w-optimal solutions, or solutions that are
no more than w times longer than the optimal solution."
★ 实测(C* = 47):W = 1 → 解 47(恰好是最优)|W = 1.25 → 47|W = 1.5 → 52|
W = 2 → 52|W = 3 → 52。五档全部落在自己的界里(W = 1.5 那一档的界是 70.5,实测 52,
C/C* = 1.106)。
★ 最能看出"旋钮"性质的是两个列的方向相反:W 从 1 拖到 3,要定的格从 233 掉到 41(省了 82%),
而解代价最多也就多 10.6%。这就是工程上真正在做的那笔交换。
★ 一个边界要写清:那个界只跟 W 有关,跟上一段的 k 无关 —— 页面上两个滑块都在,
你若把 k 拖到 0.5、W 拖到 2,界仍然是 2·C*。
W),
但本段新增的是读数里多了一格"上界"。右列两列方向相反:
已定从 233 掉到 41,解代价最多涨 10.6%。| 记的账 | 应然 | 实测 | 结论 | 怎么算 | 类别 |
|---|
定点观察:① 拖「权重 W」那个滑块(从 1 拖到 3); ② 同时看画面里深色那一块怎么继续收,以及读数里"已定"、"解代价"、"上界"三个数; ③ 结论:要定的格从 233 掉到 41,而解代价从 47 只涨到 52(+10.6%), 并且始终落在 W·C* 这条界下面 —— 这就是"故意不最优"的工程形态。
本课的收尾
这一课只补了一件事:让每格的数里多一项"到终点还要花多少"的猜测,取的时候按
f = g + h 取。四段把这一项从"是什么"问到"猜多少"、"猜过头"、"当旋钮"。
往下的缺口:猜测必须逐格算、逐格存,而且网格上还有大量互为对称的格 ——
同一对起终点、代价完全相同、只是"先横走还是先竖走"的差别。A* 会把它们一个一个评估过去。
下一课(第 3 课 · JPS)要剪掉的就是这一大批对称:
只在一部分点上记数,跳点之间的格一个都不展开(一手源原话:"Intermediate nodes on a path connecting
two jump points are never expanded.")。
★ 本页留了一个可以自己动手的口子:把「猜多少 k」拖到 0,你会看到画面退化成第 1 课那一大片
(已定从 233 涨回 774)—— 这就是"第 1 课是这一课的特例"这句话的图像。
自检面板
等待脚本…