我们知道问题求解过程实际上是一个搜索过程。为了进行搜索,首先必须用某种形式把问题表示出来,其表示是否适当,将直接影响到搜索效率。状态空间表示法就是用来表示问题及其搜索过程的一种方法。它是人工智能中最基本的形式化方法,也是讨论问题求解技术的基础。前文我们已经了解了状态空间表示法,是用“状态”和“算符”来表示问题的一种方法。其中,“状态”用以描述问题求解过程中不同时刻的状况;“算符”表示对状态的操作,算符的每一次使用就使问题由一种状态变换为另一种状态。当到达目标状态时,由初始状态到目标状态所用算符的序列就是问题的一个解。
有关“搜索策略的基本概念”的内容,看我的文章:搜索策略的基本概念-CSDN博客
由问题的全部状态及一切可用算符所构成的集合称为问题的状态空间。
状态空间的搜索策略分为盲目搜索及启发式搜索两大类。下面讨论的广度优先搜索、深度优先搜索、有界深度优先搜索、代价树的广度优先搜索以及代价树的深度优先搜索都属于盲目搜索策略。其特点是:
(1)搜索按规定的路线进行,不使用与问题有关的启发性信息。
(2)适用于其状态空间图是树状结构的一类问题。
局部择优搜索及全局择优搜索属于启发式搜索策略,搜索中要使用与问题有关的启发性信息,并以这些启发性信息指导搜索过程,可以高效地求解结构复杂的问题。
深度优先搜索,对比广度优先,使用栈结构,可能导致无限循环,需要提到深度限制。有界深度优先则是深度优先的改进,加入深度限制避免无限递归。代价树的搜索需要考虑边的权重,广度和深度搜索在代价树上的应用,比如按代价排序的队列或栈。
一、状态空间的一般搜索过程
(一)基本思想与核心要素
状态空间搜索的核心是将问题抽象为一个由状态和操作构成的图,通过探索图中的路径寻找从初始状态到目标状态的解。其核心要素包括:
(1)状态(State):问题在某一时刻的完整描述,常用向量、矩阵或结构体表示(如八数码问题的棋盘布局);
(2)算符(Operator):状态转移的合法操作(如移动、赋值、逻辑变换等);
(3)状态空间(State Space):所有可能状态及算符构成的有向图,记为三元组 (S, O, G),其中 S 是初始状态,O 是算符集合,G 是目标状态集合。
(二)搜索流程与数据结构
1. 通用搜索框架
对于一个具体问题,如何生成它所需要的部分状态空间从而实现对问题的求解呢?在人工智能中是通过运用搜索技术来解决这一问题的。其基本思想是:首先把问题的初始状态(即初始节点)作为当前状态,选择适用的算符对其进行操作,生成一组子状态(或称后继状态、后继节点、子节点),然后检查目标状态是否在其中出现。若出现,则搜索成功,找到了问题的解;若不出现,则按某种搜索策略从已生成的状态中再选一个状态作为当前状态。重复上述过程,直到目标状态出现或者不再有可供操作的状态及算符时为止。
下面列出状态空间的一般搜索过程:
(1)把初始节点S0放人OPEN表,并建立目前只包含S0的图,记为G。
(2)检查OPEN表是否为空,若为空则问题无解,退出。
(3)把OPEN表的第一个节点取出放人CLOSED表,并记该节点为节点n。
(4)考察节点n是否为目标节点。若是,则求得了问题的解,退出。
(5)扩展节点n,生成一组子节点。把其中不是节点n先辈的那些子节点记作集合M,并把这些子节点作为节点n的子节点加人G中。
(6)针对M中子节点的不同情况,分别进行如下处理:
①对于那些未曾在G中出现过的M成员设置一个指向父节点(即节点n)的指针,并把它们放入OPEN表。
②对于那些先前已在G中出现过的M成员,确定是否需要修改它指向父节点的指针。
③对于那些先前已在G中出现并且已经扩展了的M成员,确定是否需要修改其后继节点指向父节点的指针。
(7)按某种搜索策略对OPEN表中的节点进行排序。
(8)转第(2)步。
2. 关键数据结构
期间要用到两个数据结构(Open表和Closed表)。
OPEN表用于存放刚生成的节点,其形式如下表所示。对于不同的搜索策略,节点在OPEN表中的排列顺序是不同的。例如对广度优先搜索,节点按生成的顺序排列,先生成的节点排在前面,后生成的排在后面。
状态节点 | 父节点 |
CLOSED表用于存放将要扩展或者已扩展的节点,其形式如下表所示。所谓对一个节点进行“扩展”是指:用合适的算符对该节点进行操作,生成一组子节点。
编号 | 状态节点 | 父节点 |
总结:
(1)Open 表:存储待扩展状态,不同搜索策略的区别在于选择下一个扩展状态的规则(如 BFS 的队列、DFS 的栈、A * 的优先队列);
(2)Closed 表:记录已扩展状态(就是已走过的),避免重复访问,优化搜索效率。
(三)算法复杂度分析
搜索效率由以下因素决定:
(1)分支因子(b):每个状态的平均后继数;
(2)解的深度(d):初始状态到目标状态的最短路径长度;
(3)搜索策略:决定Open表的排序规则,影响搜索空间的遍历顺序。
二、广度优先搜索(Breadth-First Search, BFS)
(一)基本思想与定义
1. 核心思想
广度优先搜索又称为宽度优先搜索。
广度优先搜索的基本思想是:从初始节点S0开始,逐层地对节点进行扩展并考察它是否为目标节点,在第n层的节点没有全部扩展并考察之前,不对第n+1层的节点进行扩展。OPEN表中的节点总是按进人的先后顺序排列,先进人的节点排在前面,后进人的排在后面。
从初始状态开始,逐层扩展所有后继状态,确保先访问深度较小的状态,是一种典型的盲目搜索策略。其核心是按层遍历,保证找到最短路径(若存在)。
2. 形式化定义
(1)队列管理:Open表采用先进先出(FIFO)队列,确保同一层状态按顺序扩展;
(2)最优性:在无代价或等代价问题中,BFS是完备且最优的(找到最短路径)。
(二)表示形式与实现过程
1. 数据结构
(1)Open表:队列,按状态生成顺序排列;
(2)Closed表:集合,记录已访问状态及路径。
2. 实现步骤
其搜索过程如下:
(1)把初始节点S0放人OPEN表。
(2)如果OPEN表为空,则问题无解,退出。
(3)把OPEN表的第一个节点(记为节点n)取出放人CLOSED表。
(4)考察节点n是否为目标节点。若是,则求得了问题的解,退出。
(5)若节点n不可扩展,则转第(2)步。
(6)扩展节点n,将其子节点放人OPEN表的尾部,并为每一个子节点都配置指向父节点的指针,然后转第(2)步。
广度优先搜索流程示意图如下:
(三)算法描述(伪代码)
python代码如下:
def bfs(initial_state, goal_test):
open_queue = deque([initial_state])
closed_set = set()
parent = {initial_state: None}
while open_queue:
current_state = open_queue.popleft()
closed_set.add(current_state)
if goal_test(current_state):
return reconstruct_path(parent, current_state)
for operator in get_operators(current_state):
next_state = apply_operator(current_state, operator)
if next_state not in closed_set and next_state not in open_queue:
parent[next_state] = current_state
open_queue.append(next_state)
return None # 无解
(四)具体示例:重排九宫问题
1. 问题定义
在3×3的方格棋盘上放置分别标有数字1,2,3,4,5,6,7,8的八张牌,初始状态为S0,目标状态为Sg。
可使用的算符有:空格左移,空格上移,空格右移,空格下移,即它们只允许把位于空格左,上,右,下边的牌移人空格。要求寻找从初始状态到目标状态的路径。
(1)初始状态:
(2)目标状态:
(3)算符:空格(中间空的地方)的上、下、左、右移动,生成4个合法后继(边界状态除外),即上、下、左、右对应的8、6、1、4都可以移到空格的位置上。
应用广度优先搜索,可得到如下图所示的搜索树。
2. 搜索流程
(1)初始化:Open 表 = [S_0],Closed 表 = ∅;
(2)第1层扩展:取出 S_0,生成4个后继状态(空格右移、下移、左移、上移),合法状态4个,加入Open表;
(3)第2层扩展:依次取出队列头部状态,检查是否为目标状态,生成下一层状态(从状态2到状态6和7,这里从2到状态6、7就只有二种合法状态了,原来走过的已经在Closed表中了,所以不能继续走,要排除);
……
(4)终止条件:当某状态与 S_g 匹配时,回溯父节点得到路径。
3. 路径示例
上图经过4层扩展后找到目标状态,路径为:S_0 → S_1 → S_2 → S_3 → S_g,具体为:解的路径是S_0 → 3 → 8 → 16 → 26,对应空格移动顺序:8下移→2左移→1上移→8左移。
4. 复杂度分析
(1)时间复杂度:O(b^d),其中 b=3(平均分支因子),d=5(解的深度、最短路径长度);
(2)空间复杂度:O(b^d),需存储所有层的状态。
三、深度优先搜索(Depth-First Search, DFS)
(一)基本思想与定义
1. 核心思想
从初始状态出发,优先扩展当前状态的最深层后继,沿一条路径尽可能深入搜索,直到无法扩展或找到目标,再回溯到上一层。
深度优先搜索,对比广度优先,它使用了栈结构,可能导致无限循环,需要深度限制。
2. 形式化定义
(1)栈管理:Open表采用栈(LIFO),每次扩展最新生成的状态;
(2)不完备性:可能陷入无限循环,需结合深度限制使用。
(二)表示形式与实现过程
1. 数据结构
(1)Open表:栈,后进先出;
(2)Closed表:可选,若不使用可能重复访问状态(递归实现常省略 Closed 表,但可能导致重复计算)。
2. 实现步骤(迭代版)
(1)将初始状态 S_0 压入栈,标记父节点;
(2)若栈为空,返回无解;
(3)弹出栈顶状态 n,若为目标状态,返回路径;
(4)生成 n 的所有合法后继状态,按逆序压入栈(确保顺序正确);
(5)若 n 未访问过,加入 Closed 表,重复步骤 2-5。
(三)算法描述(伪代码)
python代码如下:
def dfs(initial_state, goal_test):
stack = [(initial_state, None)]
closed_set = set()
while stack:
current_state, parent_node = stack.pop()
if current_state in closed_set:
continue
closed_set.add(current_state)
if goal_test(current_state):
return reconstruct_path(parent_node, current_state)
# 逆序压入以保证顺序正确(如先左后右,栈顶为右)
for operator in reversed(get_operators(current_state)):
next_state = apply_operator(current_state, operator)
if next_state not in closed_set:
stack.append((next_state, current_state))
return None
(四)具体示例:迷宫搜索
1. 问题定义
网格迷宫:10×10 网格,起点 (0,0),终点 (9,9),障碍物随机分布;
算符:上、下、左、右移动,不可穿过障碍物。
2. 搜索流程
(1)初始化:栈 = [(0,0, None)];
(2)扩展路径:每次优先向下移动(假设算符顺序为下、右、上、左),直到遇到死胡同;
(3)回溯:若当前状态无合法后继,弹出栈顶,尝试其他方向;
(4)终止:找到终点或栈空。
3. 优缺点
(1)优点:内存占用小(仅存储当前路径);
(2)缺点:可能陷入深度无限的路径(如环形路径),需设置深度限制。
四、有界深度优先搜索(Bounded Depth-First Search, BDFS)
(一)基本思想
为避免DFS无限递归,设定最大搜索深度 d_{max},当搜索深度超过 d_{max} 时停止该路径扩展。是对DFS的改进,增加了搜索边界。
(二)实现过程
(1)在DFS基础上增加深度参数 depth,初始为 0;
(2)扩展状态时,若 depth ≥ d_{max},跳过该状态;
(3)其他步骤与 DFS 一致,返回 “有限深度内无解” 或路径。
流程示意图如下:
(三)示例:八数码问题深度限制
设定 d_{max}=10,若10步内未找到解,返回失败;
避免因棋盘不可解(如初始状态与目标状态奇偶性不同)导致的无限搜索。
五、代价树的广度优先搜索(Uniform-Cost Search, UCS)
(一)基本思想
当状态转移具有不同代价(如路径长度、时间、能耗等),优先扩展总代价最小的状态,是BFS的加权扩展。即在不同是算符条件下,设置不同的权值。
(二)实现过程
(1)Open表:优先队列,按总代价 g(n) 排序(g(n) 为从初始状态到 n 的累计代价);
(2)代价计算:g(next_state) = g(current_state) + cost(current_state, next_state);
(3)最优性:若代价非负,UCS 找到总代价最小的路径。
流程示意图如下:
(三)算法描述(伪代码)
python代码如下:
import heapq
def uniform_cost_search(initial_state, goal_test):
heap = [(0, initial_state, None)]
closed_set = {} # 记录最小代价
while heap:
current_cost, current_state, parent = heapq.heappop(heap)
if goal_test(current_state):
return reconstruct_path(parent, current_state)
if current_state in closed_set and closed_set[current_state] <= current_cost:
continue
closed_set[current_state] = current_cost
for operator in get_operators(current_state):
next_state = apply_operator(current_state, operator)
new_cost = current_cost + get_cost(current_state, next_state)
if next_state not in closed_set or new_cost < closed_set.get(next_state, float('inf')):
heapq.heappush(heap, (new_cost, next_state, current_state))
return None
(四)示例:最短路径问题
图结构:节点为城市,边权为距离,求从A到D的最短路径;
扩展顺序:按距离从小到大扩展(A→B:5, A→C:3, C→D:4),最终路径 A→C→D(总代价 7)。
六、代价树的深度优先搜索(Depth-First Search with Cost)
(一)基本思想
在DFS基础上考虑代价,优先扩展当前路径代价较小的后继,但不保证最优,适用于代价估计不可靠或追求速度的场景。流程示意图如下:
(二)实现差异
Open表:栈按代价排序(或不排序,仅记录代价);
缺陷:可能陷入高代价路径,导致非最优解。
七、启发式搜索(Heuristic Search)
(一)启发性信息与估价函数
1. 启发性信息
利用领域知识设计的提示信息,引导搜索优先访问更可能接近目标的状态,如:
(1)八数码问题:曼哈顿距离(当前状态与目标状态各数字位置差的绝对值之和);
(2)路径规划:直线距离(到目标的欧氏距离)。
2. 估价函数
定义为 f(n) = g(n) + h(n),其中:
g(n):从初始状态到 n 的实际代价(如步数、距离);
h(n):启发式函数,估计 n 到目标的代价(需满足非负性)。
(二)局部择优搜索(Greedy Search)
1. 基本思想
仅考虑启发式函数 h(n),每次选择当前状态中 h(n) 最小的后继扩展,类似DFS的启发式版本。流程示意图如下:
2. 算法描述
Open表:优先队列,按 h(n) 排序;
缺陷:不完备,可能陷入局部最优(如八数码问题的错误路径)。
(三)全局择优搜索(Global Best-First Search)
1. 基本思想
结合 g(n) 和 h(n),按 f(n) = g(n) + h(n) 排序,是 A * 算法的基础。
2. 示例:八数码问题的曼哈顿距离
其中 (x_i, y_i) 是当前位置,(x_i', y_i') 是目标位置;
优先扩展 f(n) = g(n) + h(n) 最小的状态,平衡已走代价和剩余代价估计。
八、A * 算法(A-Star Algorithm)
(一)A * 算法的可纳性(Admissibility)
1. 定义
若启发式函数 h(n) 满足 可采纳性(h(n) ≤ h^*(n),其中 h^*(n) 是 n 到目标的实际最小代价),则 A * 算法是可纳的,即保证找到最优解。
2. 证明要点
假设存在更优路径未被扩展,通过反证法证明 A * 必扩展最优路径上的所有状态。
(二)A * 算法的最优性
1. 最优性条件
当 h(n) 可采纳且状态空间有限,A * 算法返回总代价最小的路径。
2. 与 UCS 的关系
UCS 是 A * 算法的特例(h(n)=0);
A * 通过启发式加速,优于 UCS 在相同问题中的表现。
(三)h(x)的单调性限制
1. 单调性定义
若对所有相邻状态 n, m 成立,则 h(n) 是单调的(一致启发式)。
2. 性质
单调性确保 g(n) 单调递增,可省略 Closed 表中的代价检查,提高效率;
曼哈顿距离在八数码问题中是单调的(移动一步最多使曼哈顿距离变化 1,等于实际代价 1)。
(四)具体示例:A * 求解八数码问题
1. 估价函数
f(n) = g(n) + h(n),其中 g(n) 是移动步数,h(n) 是曼哈顿距离。
2. 搜索流程
(1)初始状态:g=0, h=5, f=5;
(2)扩展规则:每次选择 f(n) 最小的状态,生成后继并更新 g(n) 和 h(n);
(3)终止条件:目标状态的 f(n) = g(n) = 最短步数,确保最优解。
3. 优势对比
相比 BFS,A * 通过启发式剪枝减少 90% 以上的状态扩展;
单调性保证无需重复计算已访问状态的代价。
九、搜索策略对比与选择
策略 | 数据结构 | 优先级规则 | 最优性 | 复杂度 | 适用场景 |
BFS | 队列 | 层序扩展 | 是 | O(b^d) | 短路径、无代价问题 |
DFS | 栈 | 深度优先 | 否 | O(b^m) | 内存有限、不要求最优 |
UCS | 优先队列 | 代价最小 | 是 | ![]() | 代价敏感问题 |
A* | 优先队列 | f(n)=g+h | 是 | O(b^d)(启发式剪枝) | 复杂问题最优解 |
(一)复杂度说明
C^* 是最优解代价,ϵ 是启发式误差;
实际应用中,A * 的 h(n) 设计是关键,需在精度和计算成本间权衡。
十、总结与实践指南
(一)核心价值
状态空间搜索是人工智能问题求解的基础,其核心贡献在于:
(1)问题建模普适性:适用于逻辑推理、规划、游戏等多种场景;
(2)解的质量可控性:通过策略选择平衡效率与最优性(如 BFS 的完备性 vs A * 的启发式加速);
(3)启发式创新空间:领域知识通过估价函数嵌入搜索过程,推动算法性能突破(如 AlphaGo 的蒙特卡洛树搜索)。
(二)实施步骤
1.问题抽象:定义状态、算符、目标条件及代价函数;
2.策略选择:
(1)求最短路径且规模小:BFS;
(2)代价敏感且求最优:UCS 或 A*(若可设计可采纳的 h(n));
(3)内存受限:DFS 或有界 DFS;
3.启发式设计:
(1)确保 h(n) 非负且不高估实际代价(A * 可纳性条件);
(2)优先选择单调启发式(如曼哈顿距离)以提高效率;
4.优化与验证:
(1)通过 Open 表排序规则优化(如 A * 的优先队列实现);
(2)测试极端案例(如环形状态、不可解问题)验证算法鲁棒性。
(三)前沿应用
(1)自动驾驶路径规划:结合实时路况数据,用 A * 算法动态生成最优路线;
(2)蛋白质折叠预测:通过状态空间搜索探索分子结构,启发式函数基于物理化学知识;
(3)软件测试用例生成:DFS 结合有界深度搜索,覆盖更多代码路径。
状态空间搜索策略的发展始终围绕 “效率” 与 “正确性” 展开,从盲目搜索到启发式搜索,再到 A * 算法的理论完备性,每一次进步都推动着人工智能在复杂问题求解上的突破。未来,随着量子计算和神经启发式的融合,搜索策略将在更大规模、更高不确定性的场景中发挥核心作用,成为通用人工智能的重要基石。