8.3 Uniform Cost Search (均匀代价搜索)算法
均匀代价搜索(Uniform Cost Search)是一种在图中寻找最短路径的算法,是一种延伸的广度优先搜索算法。与广度优先搜索不同的是,均匀代价搜索会考虑到路径的代价,以确保找到的路径是最短的。
8.3.1 Uniform Cost Search算法的基本思想
均匀代价搜索(Uniform Cost Search)算法是一种用于在图中寻找最短路径的搜索算法。与广度优先搜索类似,它也是一种逐层扩展搜索,但是不同之处在于,均匀代价搜索考虑了路径的代价,以确保找到的路径是最短的。实现Uniform Cost Search算法的基本步骤如下所示。
(1)初始化:将起始节点放入一个优先队列中,队列按照路径代价排序,起始节点的路径代价为0。
(2)循环:从优先队列中取出路径代价最小的节点进行扩展,即选择当前路径代价最小的节点进行处理。
(3)节点扩展:对于当前节点,将其所有未被访问过的邻居节点加入到优先队列中,并更新这些邻居节点的路径代价。如果邻居节点已经在优先队列中,并且新的路径代价更小,则更新其路径代价。
(4)重复:重复以上步骤,直到找到目标节点或者优先队列为空。如果优先队列为空,表示无法到达目标节点,搜索失败。
(5)路径回溯:如果找到目标节点,通过回溯父节点的方式找到从起始节点到目标节点的最短路径。
均匀代价搜索算法的优先队列中的排序是基于路径代价的,因此它总是选择当前路径代价最小的节点进行扩展,从而保证找到的路径是最短的。与广度优先搜索不同,均匀代价搜索不仅考虑节点之间的距离,还考虑到了经过的路径的代价。
总的来说,均匀代价搜索算法是一种保证在有向图中找到最短路径的方法,适用于边的代价不同的情况下。请看下面的例子,演示了在迷宫中使用均匀代价搜索算法寻找从起点到终点的最短路径的过程。
1. 任务描述
假设你是一名探险家,现在你置身于一个神秘的迷宫中,迷宫由一个二维数组表示,数组中的0表示可以通行的空地,1表示墙壁。你的目标是找到迷宫中隐藏的宝藏,宝藏的位置在地图的右下角(即数组的最后一个元素)。
2. 题目要求
- 使用均匀代价搜索(Uniform Cost Search)算法来搜索最短路径,找到从起始位置到达宝藏的最短路径,并输出路径的长度。
- 打印输出从起始位置到达宝藏的最短路径的长度。
- 可视化展示迷宫地图和找到的最短路径,起始位置用红色标记,宝藏位置用绿色标记,路径用蓝色线条标记。
- 如果找不到到达宝藏的路径,则输出提示信息“No path found to the treasure!”。
实例8-3:使用Uniform Cost Search算法寻找迷宫中的宝藏(codes/8/Ucs.py)
实例文件Ucs.py的具体实现代码如下所示。
import matplotlib.pyplot as plt
import numpy as np
import heapq
# 迷宫地图
maze = np.array([
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0]
])
# 定义节点
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
self.g = float('inf') # 到达该节点的代价
self.parent = None
def __lt__(self, other):
return self.g < other.g
# 获取节点周围可行走的邻居
def get_neighbors(node):
neighbors = []
for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
nx, ny = node.x + dx, node.y + dy
if 0 <= nx < maze.shape[0] and 0 <= ny < maze.shape[1] and maze[nx, ny] == 0:
neighbors.append(Node(nx, ny))
return neighbors
# Uniform Cost Search算法
def uniform_cost_search(start, end):
start_node = Node(*start)
end_node = Node(*end)
start_node.g = 0
open_list = []
heapq.heappush(open_list, start_node)
while open_list:
current_node = heapq.heappop(open_list)
if current_node.x == end_node.x and current_node.y == end_node.y:
return current_node
for neighbor in get_neighbors(current_node):
new_g = current_node.g + 1 # 这里假设邻居之间的代价都是1
if new_g < neighbor.g:
neighbor.g = new_g
neighbor.parent = current_node
heapq.heappush(open_list, neighbor)
# 可视化迷宫地图和路径
def visualize_path(start, end, path):
plt.imshow(maze, cmap='binary')
plt.scatter(start[1], start[0], color='red', label='Start', s=100)
plt.scatter(end[1], end[0], color='green', label='End', s=100)
if path:
path_x = [node.y for node in path]
path_y = [node.x for node in path]
plt.plot(path_x, path_y, color='blue', label='Path')
plt.legend()
plt.show()
# 主程序
def main():
start = (0, 0)
end = (4, 4)
result = uniform_cost_search(start, end)
if result:
path = []
current_node = result
while current_node:
path.append(current_node)
current_node = current_node.parent
path.reverse()
print("The minimum cost from node A to node E is:", result.g)
visualize_path(start, end, path)
else:
print("No path found from node A to node E.")
if __name__ == "__main__":
main()
上述代码的实现流程如下所示:
- 首先,定义了一个二维数组来表示迷宫地图,其中0表示可以通行的空地,1表示墙壁。
- 然后,创建了类Node来表示节点,每个节点包含其在迷宫中的坐标以及到达该节点的代价。另外,还定义了一个比较函数实现节点排序功能。
- 接着,定义函数get_neighbors,用于获取一个节点的可行走邻居节点,即在四个方向上没有墙壁且在地图范围内的节点。
- 然后,定义函数uniform_cost_search实现Uniform Cost Search算法,用于寻找从起始位置到达宝藏的最短路径。该算法通过一个优先队列来扩展节点,并不断更新到达每个节点的最小代价。
- 最后,编写了可视化函数visualize_path,功能是可视化展示迷宫地图和找到的最短路径。
执行后会打印输出如下从起始位置到达宝藏的最短路径的长度,并绘制如图8-2所示的可视化图。其中起始位置用红色标记,宝藏位置用绿色标记,路径用蓝色线条标记。
The minimum cost from node A to node E is: 8
图8-2 路径可视化图