python 实现A*算法的示例代码
A*算法是一种启发式搜索算法,经常用于路径规划问题中。它的特点是能够快速搜索出一条路径,并且保证路径是最优解或者是最优解的近似解。在本文中,我们将介绍如何使用Python来实现A*算法,并给出一个简单的示例代码。
1. A*算法的基本思路
A*算法是一种基于优先级搜索的算法,其基本思路如下:
1.1 定义启发函数f(n)
在A*算法中,启发函数被定义为f(n)。它用于评估每个节点n的优先级。f(n)的计算方式通常有两种:
- f(n) = g(n) + h(n)
其中g(n)是从起始节点到节点n的实际距离(也称为实际代价),h(n)是从节点n到目标节点的估计距离(也称为启发代价)。h(n)是一种估价函数,它并不是节点n到目标节点的实际距离,而是基于一些启发式的方法进行计算的。在A*算法中,h(n)必须满足以下条件:
- h(n) >= 0
- h(n) <= h*(n)
其中h*(n)是节点n到目标节点的实际距离或者是实际代价的上界。通常情况下,h*(n)不能超过实际距离的两倍。
- f(n) = g(n)
在某些情况下,节点n到目标节点的距离是已知的,此时可以将f(n)简化为g(n)。例如,在棋盘上搜索,如果每个节点都是正方形,则h(n)等于从节点n到目标节点的最短曼哈顿距离。此时,f(n)可以表示为:
f(n) = g(n) + h(n),
其中:
g(n)表示在起点创建节点n的代价,
h(n)表示节点n到终点的最短距离(曼哈顿距离)。
另一方面,如果每个节点不是一个正方形,则用勾股定理计算代价。
1.2 初始化起始节点和目标节点
我们需要先初始化起始节点(也称为起点)和目标节点(也称为终点)。这两个节点可以通过在地图上点选或者手动指定。在A*算法中,起始节点是起点,目标节点是终点。每次搜索都是从起点开始,直到到达终点为止。
1.3 将起始节点加入开放列表
开发列表是用于存储搜索结果的数据结构。在A*算法中,开放列表是一个按优先级排序的节点列表。搜索从起点开始,从开放列表中取出优先级最高的节点。由于起点本身是一个节点,所以我们需要将起始节点加入开放列表。
1.4 开始搜索
在搜索过程中,我们需要不断取出开放列表中优先级最高的节点,直到到达终点。在取出一个节点n之后,我们将其加入关闭列表(已访问节点列表)。然后,我们需要找到所有与节点n相邻的节点,并计算它们的f(n)值。对于每个相邻节点k,如果它没有被访问过,将其添加到开放列表中。
1.5 判断节点是否已被访问过
节点是否已经被访问过,可以通过检查其是否在关闭列表中进行判断。如果节点n已经在关闭列表中,则说明我们已经找到了从起点到节点n的最短路径。
1.6 计算路径
在每次访问节点时,我们需要计算从起点到当前节点的路径。对于当前节点k,其所有祖先节点(也称为父节点)可以通过从k的父节点开始递归计算而得到。直到到达起点为止。如果我们找到了从起点到终点的最短路径,在计算完成后,我们可以从终点开始递归,找到所有的祖先节点,并将其组成一条路径。
2. Python实现
下面,我们将使用Python来实现A*算法,代码示例如下:
from queue import PriorityQueue
def astar(start, goal, successor, heuristic):
# create open and closed lists
open_list = PriorityQueue()
closed_list = set()
# initialize start node
start_node = {"parent": None, "position": start, "g": 0, "f": 0}
open_list.put((0, id(start_node), start_node))
# search the path
while not open_list.empty():
# get the node with the highest priority
current_node = open_list.get()[2]
# check if the goal is reached
if current_node["position"] == goal:
# construct the path
path = []
while current_node:
path.append(current_node["position"])
current_node = current_node["parent"]
return path[::-1]
# add current node to closed list
closed_list.add(id(current_node))
# generate successor nodes
for next_node in successor(current_node["position"]):
# calculate g and f values
new_g = current_node["g"] + 1
new_f = new_g + heuristic(next_node, goal)
next_node_dict = {"parent": current_node, "position": next_node, "g": new_g, "f": new_f}
next_node_id = id(next_node_dict)
# check if next node is already explored
if next_node_id in closed_list:
continue
# check if next node is already in the open list
for _, _, node in open_list.queue:
if id(node) == next_node_id and next_node_dict["g"] > node["g"]:
continue
# add next node to the open list
open_list.put((new_f, next_node_id, next_node_dict))
# if the open list is empty and goal is not reached
return None
# example usage
start = (0, 0)
goal = (5, 7)
def manhattan_distance(a, b):
return abs(a[0] - b[0]) + abs(a[1] - b[1])
def successor(node):
x, y = node
candidates = [(x+1, y), (x-1, y), (x, y+1), (x, y-1)]
return [(x, y) for x, y in candidates if 0 <= x < 6 and 0 <= y < 8]
path = astar(start, goal, successor, manhattan_distance)
print(path)
在这个示例中,我们需要传入四个参数。start和goal分别代表起点和终点。successor是一个函数,用于生成与节点相邻的所有节点。heuristic是一个估价函数,用于评估节点与目标节点之间的距离。在示例中,我们使用了曼哈顿距离作为估计距离。同时在函数中,我们定义了一个open_list和closed_list,分别用于存储已经访问和未访问的节点。
3. 小结
本文介绍了A*算法的基本思路和Python实现。虽然这个算法在路径规划中被广泛使用,但是实现起来并不复杂。通过使用Python优秀的数据结构和算法库,我们可以很容易地实现一个高效的路径规划算法。
