Background
1 A*(A Star)Algorithm
A算法是一种常用于图搜索和路径规划的启发式搜索算法。它利用启发式函数(heuristic function)来评估每个节点的价值,并据此来指导搜索过程,以便在可能的搜索空间中尽快找到最优解。A算法结合了Dijkstra算法的无向图搜索和贪婪最优化搜索的优点,同时避免了它们的缺点。
算法思想
A*算法的基本思想包括以下几点:
-
启发式函数(Heuristic Function):A*算法使用启发式函数来评估每个节点到目标节点的估计距离。这个启发式函数提供了一种启发式的估计,用来指导搜索的方向,使得算法更有可能沿着最有希望的路径前进。
-
综合考虑路径代价:A*算法同时考虑了从起始节点到当前节点的实际代价(通常用G值表示)和从当前节点到目标节点的估计代价(通常用H值表示)。它通过计算这两个代价的和(F值)来选择要探索的下一个节点。
-
贪婪搜索:A算法在每一步都选择F值最小的节点进行扩展,这使得算法更有可能朝着目标节点的方向前进。这种贪婪搜索的思想使得A算法更加高效。
A* 算法的一般表达式
假设我们有一个图,其中包含节点(nodes)和连接这些节点的边(edges)。每个边可能具有权重(cost)。
- :表示从初始节点到节点 的实际代价(即起始节点到节点 的路径长度)。
- :表示从节点 到目标节点的估计代价。这通常是一个启发式函数,用来估计从节点 到目标节点的最短距离。这个函数在A*算法中至关重要。
- :表示通过节点 的估计总代价。
A*算法的基本原理是在搜索过程中,秉承贪婪算法思想每次选择 最小的节点进行扩展。这样做可以在保证搜索最优解的同时,尽可能地减少搜索空间。
* 初始化open_list和close_list;
* 将起点加入open_list中,并设置优先级为0(优先级最高);
* 如果open_list不为空,则从open_list中选取优先级最高的节点n:
* 如果节点n为终点,则:
* 从终点开始逐步追踪parent节点,一直达到起点;
* 返回找到的结果路径,算法结束;
* 如果节点n不是终点,则:
* 将节点n从open_list中删除,并加入close_list中;
* 遍历节点n所有的邻近节点:
* 如果邻近节点m在close_list中,则:
* 跳过,选取下一个邻近节点
* 如果邻近节点m也不在open_list中,则:
* 设置节点m的parent为节点n
* 计算节点m的优先级
* 将节点m加入open_list中
A* 算法的演示例子
假设我们有一个网格地图,其中包含起始节点、目标节点和一些障碍物。我们的目标是找到从起始节点到目标节点的最短路径。
假设我们的地图如下所示:
S - - - -
- X X - -
- - - - -
- X X X -
- - - E -
其中,S是起始节点,E是目标节点,X表示障碍物,-表示可以移动的空格。
我们可以用以下简化的距离来估计到目标节点的距离(H值):
- 欧几里得距离(Euclidean distance):,表示目标节点坐标,表示当前节点坐标。
- 曼哈顿距离(Manhattan distance):
我们选择曼哈顿距离作为启发式函数。
接下来,我们按照A*算法的步骤进行搜索:
- 将起始节点加入开放列表,开始搜索过程。
开放列表(open list):[(1,1)]
闭合列表(closed list):[]
选择当前节点S,并将其从开放列表移除。检查其邻居节点:
当前节点S的邻居节点有:右边、下边。计算这些邻居节点的F、G、H值:
- 右边节点:G=1(起始点到右边节点的实际代价),H=7(目标节点到右边节点的估计代价,使用曼哈顿距离),F=8。
- 下边节点:G=1,H=6,F=7。
将这些节点加入开放列表,并选择F值最小的节点作为当前节点。则有:
开放列表:[(1,2), (2,1)]
闭合列表:[(1,1)]
- - - - -
S X X - -
- - - - -
- X X X -
- - - E -
- 继续搜索。
当前节点下边节点的邻居节点有:右边、下边。计算这些邻居节点的F、G、H值:
- 右边节点:G=2,H=6,F=8。
- 下边节点:G=2,H=5,F=7。
更新右边节点的F值,并选择F值最小的节点作为当前节点。
开放列表:[右边节点]
闭合列表:[S, 下边节点]
当前节点:右边节点
继续搜索。
- - - - -
- X X - -
- - S - -
- X X X -
- - - E -
右边节点的邻居节点有:下边。计算下边节点的F、G、H值:
- 下边节点:G=3,H=5,F=8。 更新右边节点的F值,并选择F值最小的节点作为当前节点。
开放列表:[]
闭合列表:[S, 下边节点, 右边节点]
当前节点:下边节点
继续搜索,直到找到目标节点或者开放列表为空。 在本例中,A*算法会继续搜索,直到找到目标节点E为止。如果搜索失败,则表示不存在从起始节点到目标节点的路径。
这个例子说明了A*算法是如何在地图中搜索最短路径的过程。
A* 算法代码实现
# 定义节点类
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
self.g = 0
self.h = 0
self.f = 0
self.parent = None
# 定义A*算法函数
def astar(grid, start, end):
# 定义移动方向
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
# 获取网格大小
rows = len(grid)
cols = len(grid[0])
# 创建起始节点和目标节点
start_node = Node(start[0], start[1])
end_node = Node(end[0], end[1])
# 创建开放列表和闭合列表
open_list = []
closed_list = set()
# 将起始节点加入开放列表
open_list.append(start_node)
while open_list:
# 弹出开放列表中f最小的节点
current_node = min(open_list,key=lambda node:node.f)
open_list.remove(current_node)
# 将当前节点加入闭合列表
closed_list.add((current_node.x, current_node.y))
# 如果当前节点是目标节点,返回路径
if current_node.x == end_node.x and current_node.y == end_node.y:
path = []
while current_node:
path.append((current_node.x, current_node.y))
current_node = current_node.parent
return path[::-1]
# 遍历当前节点的邻居节点
for dx, dy in directions:
new_x = current_node.x + dx
new_y = current_node.y + dy
# 确保邻居节点在网格范围内,且是可行节点,且不在闭合列表
if 0 <= new_x < rows and 0 <= new_y < cols and grid[new_x][new_y] != 'X' and (new_x, new_y) not in closed_list:
neighbor = Node(new_x, new_y)
# 不能回到走过的地方
if neighbor in closed_list:
break
# 如果邻居节点不在开放列表中
elif neighbor not in open_list:
# heapq.heappush(open_list, neighbor)
open_list.append(neighbor)
# 创建邻居节点
neighbor.g = current_node.g + 1
neighbor.h = abs(end_node.x - new_x) + abs(end_node.y - new_y) # 曼哈顿距离作为启发式函数
neighbor.f = neighbor.g + neighbor.h
neighbor.parent = current_node
# 如果开放列表为空但仍未找到路径,返回空列表
return []
# 测试样例
if __name__ == "__main__":
grid = [
['S', '-', '-', '-', '-'],
['-', 'X', '-', 'X', '-'],
['-', '-', '-', '-', '-'],
['X', '-', '-', '-', 'X'],
['-', '-', 'X', '-', '-'],
['-', 'X', '-', 'X', '-'],
['-', 'X', '-', 'X', '-'],
['-', '-', 'E', '-', '-'],
['-', '-', '-', '-', '-']
]
start = (0, 0)
end = (7, 2)
path = astar(grid, start, end)
if path:
print("找到最短路径:", path)
else:
print("未找到路径")