【发布时间】:2020-06-23 09:36:03
【问题描述】:
A* 算法是一种类似于 Dijkstra 算法的寻路算法,其工作原理是访问节点(使用启发式方法来决定接下来访问哪个节点),并将该节点与已访问且处于封闭状态的节点进行比较列表。
在我的实现中,随着封闭列表大小的增加,每秒访问的节点数急剧减少。虽然最初,该算法访问大约 3,000 个节点/秒,但随着封闭列表增长 > 10,000 个节点,这将减少到小于 50 个节点/秒。唯一在计算上变得更昂贵的是新节点与开放列表和封闭列表的比较,并将新节点存储在封闭列表中。
因此,我认为通过以更有效的方式存储封闭列表可以显着提高性能!
以下是我实施的一些摘录。一是Node类,用于定义所有Node:
class Node:
"""
A node class for A* Pathfinding
"""
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
self.g = 0 # g = actual cost of reaching this node
self.h = 0 # h = heuristic, used for determining which node to visit next
self.f = 0 # f = g + h
def __eq__(self, other):
return self.position == other.position
# defining less than for purposes of heap queue
def __lt__(self, other):
return self.f < other.f
# defining greater than for purposes of heap queue
def __gt__(self, other):
return self.f > other.f
我使用堆队列来存储打开列表,因为我认为这可以提高速度。然而,它只是略微(±5%)。
下面是我的 A* 实现,精简后只包含相关操作:
def find_a_star_path(self, current_pos, target_pos):
# Initialize start- and end-nodes with zero cost
start_node = self.Node(None, current_pos)
start_node.g = start_node.h = start_node.f = 0.0
end_node = self.Node(None, target_pos)
end_node.g = end_node.h = end_node.f = 0.0
# Initialize open- and closed list
open_list = []
closed_list = []
# Heapify the open_list and Add the start node
heapq.heapify(open_list)
heapq.heappush(open_list, start_node)
# As long as there are "open" nodes, we continue A*.
while len(open_list) > 0:
# Find node with the lowest cost F, this is visited next
current_node = heapq.heappop(open_list)
closed_list.append(current_node)
if current_node == end_node:
# if current_node = end_node, the process is finished.
# Some code that finds all possible next nodes from the next node
# This next node is called child
# child.g, child.h and child.f are calculated
# Now check if the new node is better than another node with the same position but a different parent.
filtered_open_nodes = (open_node for open_node in open_list if child == open_node)
open_node = next(filtered_open_nodes, None)
while open_node:
if child.f > open_node.f:
add_to_open = False
break
else:
# The new node is better than the other path to this node, so remove it.
open_list.remove(open_node)
open_node = next(filtered_open_nodes, None)
if add_to_open == True:
heapq.heappush(open_list, child)
【问题讨论】:
-
摆脱那些开放式和封闭式列表。只需保留已访问节点的
set(!) 并检查从堆中弹出的节点是否已经在该集合中。heap和set是快速的数据结构,列表很慢。 -
性能问题的一般方法:给自己找一个像样的分析器(希望 Python 有这样的工具)来找出哪一行代码正在消耗 CPU 时间。在您的情况下,很可能是 open_list 中的顺序搜索,但总的来说:使用分析器!
标签: python performance heap a-star