搜索与状态空间
现实中的许多智能问题——下棋、路径规划、定理证明、魔方复原——都没有直接套用的公式,而需要在大量可能的选择中试探。这种为了达到目标,不断从可用操作中选择并执行、直到找到解答的过程,就是搜索(Search)。搜索是问题求解的主要手段,也是推理过程的内在机制。
状态空间表示法
状态空间法把问题形式化为三元组 (S, F, G):
- 状态(State):描述问题在某一时刻所处格局的数据结构,记为状态变量的有序集合
Q = (q1, q2, …, qn)。S 是所有可能状态的集合; - 算符(Operator):使问题从一种状态变为另一种状态的操作(走法、规则、动作),F 是算符集合;
- 目标(Goal):G ⊆ S 是满足终止条件的目标状态集合。
状态空间可直观地表示为一张有向图:节点是状态,边是算符。问题求解就是在图中寻找一条从初始状态 S0 到某个目标状态的路径。以 15 数码(4×4 滑块)为例:一种盘面排列是一个状态,"空格上/下/左/右移动"是四个算符,目标是还原成有序盘面。
3×3 棋盘上有 8 个数字块和一个空格,每次只能把与空格相邻的数字移入空格。
初始状态:2 8 3 / 1 □ 4 / 7 6 5 目标状态:1 2 3 / 8 □ 4 / 7 6 5
搜索树从初始状态展开:空格上移、左移、右移分别产生 2~3 个后继节点,逐层扩展直到出现目标状态,回溯即得移动序列。
问题归约表示法
另一种思路是"分而治之":把复杂问题分解为若干较简单的子问题,子问题再继续分解,直到成为无需再分、可直接求解的"本原问题"。子问题间存在"与"(全部要解出)和"或"(任选其一)关系,整体结构表示为与或图(与或树)。例如证明几何题时,可把"证明两三角形全等"归约为"证明三边相等 或 两边及夹角相等 或 两角及一边相等",而每个条件本身又是一个需要证明的子问题。
- 盲目搜索(无信息搜索):不依赖问题本身的知识,按预先固定的次序展开节点——广度优先、深度优先、代价一致;
- 启发式搜索(有信息搜索):利用反映问题特点的启发信息(估价函数)决定展开顺序——贪心最佳优先、A 算法、A* 算法;
- 按搜索对象还可分为状态空间搜索(普通或图)与与或树搜索;对抗情形下则是博弈树搜索。
无论哪种搜索,算法通常维护两张表:OPEN 表存放已生成但尚未考察的节点,CLOSED 表存放已考察过的节点;搜索策略的差异本质上就是"OPEN 表按什么顺序取出下一个节点"的差异。
盲目搜索
广度优先搜索(BFS)
从初始节点开始,先扩展同一层的所有节点,再扩展下一层。实现上把新节点放在 OPEN 表末端(队列 FIFO)。
- 特点:逐层推进,像水波一样扩散;
- 性质:在每步代价相同的情况下,一定能找到最短路径(最优解),是完备的;
- 代价:节点数随深度指数增长,空间占用大,是典型的"时间换完备"。
深度优先搜索(DFS)
每次都向最新生成、最深的节点方向探索,走到尽头再回溯。实现上把新节点放在 OPEN 表前端(栈 LIFO)。
- 特点:一条路走到黑,存储只需保存当前路径上的节点,空间开销小;
- 性质:若图中存在无限深支路,可能陷入其中而找不到解——不完备;找到的解也不一定最短;
- 改进:有界深度优先设定深度界限 dm,超限即强制回溯,兼顾二者。
代价一致搜索(Dijkstra 思想)
当各算符代价不同(路程、费用不等)时,每次从 OPEN 表中取出从起点累计代价 g(n) 最小的节点扩展。它是广度优先在加权图上的推广,能找到总代价最小的最优解。
| 策略 | OPEN 表次序 | 完备性 | 最优性 | 空间开销 | 适用场合 |
|---|---|---|---|---|---|
| 广度优先 BFS | 新节点入队尾 | 完备 | 等代价时最优 | 大(指数级) | 深度浅、需最短路径 |
| 深度优先 DFS | 新节点入队首 | 不完备 | 不保证最优 | 小(线性) | 解在深处、只需一解 |
| 有界深度 | DFS + 深度限制 | 界内完备 | 不保证 | 较小 | 已知解的深度上限 |
| 代价一致 | 取 g(n) 最小者 | 完备 | 最优 | 大 | 边代价不等的图 |
盲目搜索的共同问题是不利用任何问题知识:八数码中"哪个盘面更接近目标"这样显而易见的信息被完全浪费,搜索树迅速膨胀。启发式搜索正是为解决这一问题而生。
启发式搜索与 A* 算法
启发式(Heuristic)即"凭经验发现"的方法。启发式搜索把与问题有关的经验性知识编码为估价函数,让搜索优先朝"最有希望"的方向推进,从而大幅减少被考察的节点数。
估价函数
估价函数 f(n) 用于估计从起点经过节点 n 到达目标的"希望程度",经典形式为:
g(n):从初始节点到 n 的实际代价(回望,已知) · h(n):从 n 到目标节点的估计代价(前瞻,启发函数)
每次从 OPEN 表中选 f(n) 最小的节点扩展。g(n) 与 h(n) 的权重决定了搜索风格:
- 只看 g(n)(h=0)→ 退化为代价一致搜索,稳但慢;
- 只看 h(n)(g=0)→ 贪心最佳优先搜索,朝着目标直线俯冲,快但可能被障碍误导而不最优、不完备;
- 二者兼顾 → A 算法;当 h(n) 满足可采纳条件时,即为 A* 算法。
h1:不在位将牌数——统计当前棋面上有多少数字块不在目标位置。每步至多归位一块,故它不超过实际所需步数。
h2:曼哈顿距离之和——把每个数字块沿水平/竖直方向搬到目标位置所需步数相加:h2 = Σ ( |xi−xi*| + |yi−yi*| )。它信息量更大,引导搜索更"锋利"。
A* 算法及其可采纳性
设 h*(n) 为从 n 到目标的真实最小代价。若启发函数满足:
则称 h 是可采纳的(Admissible),相应的 A 算法称为 A* 算法。在树搜索/配合 CLOSED 表的图搜索中,A* 保证一定找到最优解,并且只要解存在就能找到(完备)。直观理解:h 从不夸大剩余代价,因此真正最优路径上的节点不会被错误地排到队尾。
进一步,若两个启发函数满足 ha(n) ≤ hb(n) ≤ h*(n),则称 hb 比 ha 更有信息量(占优)——用 hb 搜索扩展的节点不会更多。曼哈顿距离对不在位将牌数就是占优的。常见可采纳启发函数还有网格寻路中的曼哈顿距离(四方向移动)、欧几里得直线距离(可任意方向移动)。
- OPEN 表按 f(n)=g(n)+h(n) 升序组织,每次取 f 最小者扩展;
- 发现到已在 OPEN/CLOSED 中节点的更优 g 值时,更新其父指针与 f 值,必要时"重开"该节点;
- 目标节点出队时算法结束(注意:是取出目标时才终止,而非生成时),沿父指针回溯即最优路径。
A* 的思想广泛用于游戏寻路(NavMesh、Waypoint)、机器人路径规划、GPS 导航等。其主要代价是 OPEN 表通常需要优先队列维护,且大规模地图上空间开销可观;IDA*(迭代加深 A*)、加权 A*、Jump Point Search 等是常见改进。
与或树搜索
对于问题归约得到的与或树,"找到一个节点"不再等同于"问题得解":一个或节点只要有一个子节点可解即可;而一个与节点的所有子节点都必须可解。由此需要在与或树上寻找解树(解图),并用代价评估各解树的优劣。
| 可解性 | 判定规则(自叶向根标记) |
|---|---|
| 可解节点 | ① 终止节点(本原问题)可解;② 或节点至少有一个子节点可解;③ 与节点的全部子节点均可解。 |
| 不可解节点 | ① 叶节点不是终止节点且不可扩展;② 或节点的所有子节点均不可解;③ 与节点至少有一个子节点不可解。 |
解树的代价常用两种定义(设 c(n, ni) 为边代价,叶节点代价为 0 或给定 h 值):
- 和代价:与节点 h(n) = Σ[ c(n, ni) + h(ni) ];或节点取使总代价最小的一支;
- 最大代价:与节点 h(n) = maxi[ c(n, ni) + h(ni) ];或节点同样取最优分支。
与或树的有序搜索(AO* 算法)每次选择当前最有希望的局部解图扩展,并自下而上反复修正节点代价与"最佳子节点"指针,直到选出完整解图。AO* 与 A* 的关键差异在于:与节点要求多支并行求解,代价信息必须沿解图整体回传,故不能简单套用单路径的 A*。
博弈树搜索
下棋是典型的对抗性搜索:对手会千方百计破坏你的计划。在双人零和、信息完全、轮流行动的博弈(如象棋、围棋、井字棋)中,可以把对弈过程组织成一棵博弈树:我方节点(MAX 节点)选择使评估值最大的走法,对方节点(MIN 节点)选择使评估值最小的走法。
极大极小分析法(Minimax)
① 设定静态评估函数 e(p)
对一个盘面 p 打分:对我方越有利分越高(如棋子子力、机动性、棋型加权)。
② 生成博弈树
从当前盘面交替展开我方层与对方层,直到预定深度(搜索到叶子)。
③ 自底向上倒推分值
MIN 层取子节点最小值(对手最狠的应对),MAX 层取子节点最大值(我方最佳应对)。
④ 选择行动
根节点选择倒推值最大的走法落子。
α-β 剪枝
完整 Minimax 树的节点数随深度呈指数增长。α-β 剪枝在不改变结果的前提下跳过那些绝不可能影响最终决策的分支:
- α 值:MAX 节点目前能够保证获得的最高分值(下界);
- β 值:MIN 节点目前能够保证压到的最低分值(上界);
- α 剪枝:在 MIN 节点中,若其当前值 ≤ 祖先 MAX 的 α,则该分支再小也不会被选中,剪掉;
- β 剪枝:在 MAX 节点中,若其当前值 ≥ 祖先 MIN 的 β,则该分支再大也无意义,剪掉。
着法生成顺序越合理(好棋优先),剪枝效率越高,理想情况下同样时间内可搜索深度约增加一倍。深蓝、象棋引擎主要依赖"α-β 剪枝 + 高效评估函数 + 开局库/残局库";而围棋状态空间过大、难以构造静态评估,AlphaGo 改用蒙特卡洛树搜索(MCTS)结合深度神经网络来选择落子。
交互演示:迷宫寻路
下面的演示把本章四种搜索算法放进同一张网格地图:绿色 S 为起点,红色 G 为终点,深灰方块为不可通行的障碍。淡金色方块表示进入 OPEN 表(待考察)的节点,青绿色方块表示已进入 CLOSED 表(已扩展)的节点,金色折线为最终路径。你可以亲手绘制障碍、移动起止点,并对比四种算法的扩展节点数与路径长度。
搜索算法可视化
CANVAS · BFS / DFS / GREEDY / A*① 在预置迷宫上分别运行四种算法:BFS 与 A* 找到的路径长度如何?贪心搜索扩展的节点更少,但路径是否最短?为什么?(提示:贪心只看 h(n),被墙挡住时会绕远路。)② DFS 的探索轨迹有什么特点?它找到的路径长度是否稳定?③ 试着在 S 与 G 之间画一道带缺口的长墙,观察 A* 如何在 g(n) 的约束下"回头"寻找缺口——这正是它区别于贪心搜索之处。