8.2 Best-First Search (最佳优先搜索)算法
Best-First Search(最佳优先搜索)是一种启发式搜索算法,它每次扩展具有最小启发式值的节点。与传统的深度优先搜索或广度优先搜索不同,最佳优先搜索不考虑已经走过的路径,只是简单地选择当前最有希望的节点进行扩展。
8.2.1 Best-First Search算法的基本思想
最佳优先搜索(Best-First Search)的基本思想是每次扩展具有最小启发式值的节点,直到找到目标节点或者达到搜索的终止条件。这个算法通常用于解决最优化问题,例如在路径规划中寻找最短路径或者在游戏中寻找最佳解。在搜索过程中,最佳优先搜索不保留之前访问的节点,因此可能会陷入无限循环或者无法找到解的情况,但它通常能够以较快的速度找到一个接近最优解的解。
最佳优先搜索算法的基本思路如下所示。
(1)启发式评估:在搜索过程中,每个节点都会被分配一个启发式值,这个值是根据一个启发式函数计算得到的。启发式函数用来估计从当前节点到目标节点的代价或者潜在价值,通常是一个启发性的估计,并不一定是准确的。
(2)节点扩展:算法每次选择具有最小启发式值的节点进行扩展。换句话说,它倾向于首先探索那些离目标节点最近的节点,希望能够尽快找到解决方案。这就是为什么它被称为“最佳”优先搜索,因为它总是选择当前看起来最有希望的路径。
(3)搜索终止:算法会一直扩展节点直到找到目标节点,或者搜索空间已经完全被探索,没有更多的节点可供扩展。在前者情况下,会找到解决方案;而在后者情况下,可能意味着没有找到解决方案,或者搜索空间太大,算法无法在可接受的时间内完成搜索。
在实现最佳优先搜索时,通常需要定义一个启发式函数(heuristic function),用于评估每个节点的潜在价值。这个启发式函数通常是一种估计,它估计从当前节点到目标节点的代价。基于这个启发式函数的值,算法可以选择具有最小启发式值的节点进行扩展。
总的来说,最佳优先搜索算法的基本思想是在搜索过程中根据节点的启发式值选择下一个要扩展的节点,以期望能够更快地找到解决方案。然而,需要注意的是,由于启发式函数可能并不总是准确的,因此最佳优先搜索并不保证总是能够找到最优解,而可能会停留在局部最优解。
注意:最佳优先搜索的一个常见应用是在人工智能的领域,特别是在问题求解、规划和游戏中。例如,在迷宫寻路问题中,可以使用最佳优先搜索来找到从起点到终点的最短路径。然而,需要注意的是,最佳优先搜索并不保证总是能够找到最优解,因为它只是简单地选择当前最有希望的节点进行扩展,而不考虑已经走过的路径。
8.2.2 Best-First Search算法的实现步骤
具体来说,实现Best-First Search算法的基本步骤如下所示。
(1)定义节点结构:创建一个表示节点的结构体或类,其中包括节点的位置坐标、距离起点的实际代价、到目标节点的启发式值等信息。此外,需要重载比较运算符,以便在优先队列中对节点进行排序。
(2)初始化起始节点:将起始节点添加到优先队列中,初始化其实际代价和启发式值。
(3)循环搜索:不断从优先队列中取出具有最小启发式值的节点进行扩展,直到找到目标节点或者搜索空间已经完全探索。
(4)扩展节点:对于当前节点,根据问题的规则生成所有可能的后继节点,并计算它们的实际代价和启发式值。将这些后继节点加入到优先队列中,按照启发式值的大小进行排序。
(5)判断终止条件:检查每次从优先队列中取出的节点是否为目标节点,如果是,则算法结束;如果优先队列为空,表示没有找到目标节点,算法结束。
(6)标记节点状态:在搜索过程中,需要记录已经访问过的节点,以避免重复访问和陷入循环。可以使用一个标记数组或者哈希表来记录节点的访问状态。
(7)返回结果:如果找到了目标节点,可以返回到达目标节点的最短路径或者最优解的相关信息。如果搜索失败,可以返回相应的失败信息或者特定的错误代码。
以上步骤是Best-First Search算法的一般实现过程。在具体应用中,可能需要根据问题的特点进行适当的调整和优化。例如下面是一个简单的Python例子,演示了使用最佳优先搜索算法来解决迷宫寻路问题的过程。在这个例子中,我们假设迷宫是一个二维数组,其中 0 表示可以通过的空格,1 表示障碍物。我们的目标是从起点 (startX, startY) 到达终点 (endX, endY)。
实例8-1:使用Best-First Search算法解决迷宫问题(codes/8/Mi.py)
实例文件Mi.py的具体实现代码如下所示。
import heapq
# 定义迷宫的大小
ROW = 5
COL = 5
# 定义节点类
class Node:
def __init__(self, x, y, distance, heuristic):
self.x = x # 节点坐标 x
self.y = y # 节点坐标 y
self.distance = distance # 起点到当前节点的距离
self.heuristic = heuristic # 启发式值
# 重载小于运算符,用于优先队列的排序
def __lt__(self, other):
return (self.distance + self.heuristic) < (other.distance + other.heuristic)
# 定义最佳优先搜索算法
def best_first_search(maze, startX, startY, endX, endY):
# 定义四个方向的移动偏移量
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# 定义优先队列,用于节点扩展
pq = []
heapq.heappush(pq, Node(startX, startY, 0, abs(endX - startX) + abs(endY - startY)))
# 创建一个二维数组,用于记录节点是否被访问过
visited = [[False for _ in range(COL)] for _ in range(ROW)]
while pq:
# 取出当前优先级最高的节点
current = heapq.heappop(pq)
x, y, distance = current.x, current.y, current.distance
# 到达终点,返回距离
if x == endX and y == endY:
return distance
# 将当前节点标记为已访问
visited[x][y] = True
# 在四个方向上尝试扩展节点
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
# 如果新节点在迷宫范围内且未被访问过且可通行,则加入优先队列
if 0 <= nx < ROW and 0 <= ny < COL and not visited[nx][ny] and maze[nx][ny] == 0:
heapq.heappush(pq, Node(nx, ny, distance + 1, abs(endX - nx) + abs(endY - ny)))
# 没有找到路径,返回 -1
return -1
def main():
maze = [
[0, 1, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 1, 0]
]
startX, startY = 0, 0
endX, endY = 4, 4
distance = best_first_search(maze, startX, startY, endX, endY)
if distance == -1:
print("No path found.")
else:
print("Shortest distance from start to end:", distance)
if __name__ == "__main__":
main()
对上述代码的实现流程如下所示。
- 首先,设置了迷宫的大小,其中 ROW 表示迷宫的行数,COL 表示迷宫的列数。
- 然后,定义了一个节点类 Node,用于表示搜索过程中的节点。每个节点包含四个属性:x 和 y 表示节点的坐标,distance 表示起点到当前节点的距离,heuristic 表示当前节点的启发式值。
- 接着,实现了最佳优先搜索算法 best_first_search。该算法接收迷宫 maze、起点坐标 (startX, startY)、终点坐标 (endX, endY) 作为输入,并返回起点到终点的最短路径长度。算法使用优先队列 pq 来扩展节点,并利用启发式值来选择下一个扩展的节点。在搜索过程中,通过二维数组 visited 记录已经访问过的节点,避免重复访问。
- 最后,实现了 main 函数,用于定义迷宫地图并调用 best_first_search 函数进行搜索。如果找到了路径,则输出最短路径的长度;如果没有找到路径,则输出提示信息 "No path found."。
整体上,本实例实现了使用最佳优先搜索算法解决迷宫最短路径问题的功能。执行后会输出:
Shortest distance from start to end: 8