
本文深入探讨a*寻路算法中open列表和closed列表的作用及其实现机制。通过对比一个简洁的python实现与传统伪代码,我们将分析python代码如何巧妙地通过初始化分数和更新逻辑,在不显式使用closed列表的情况下,达到与传统双列表方法相同的效果,确保算法的正确性和效率。
A算法是一种广泛应用于路径搜索和图遍历的启发式搜索算法。它通过结合实际代价(g_score,从起点到当前节点的代价)和启发式估计代价(h_score,从当前节点到目标节点的估计代价),来计算每个节点的总估计代价(f_score = g_score + h_score)。A算法的目标是找到从起点到目标点的最短路径。
为了有效地进行搜索,A*算法通常维护两个关键的数据结构:
许多A*算法的伪代码实现会明确地使用OPEN列表(优先队列)和CLOSED列表(集合),如下所示:
OPEN = 包含起点的优先队列
CLOSED = 空集
当 OPEN 中最低排名(f_score)的节点不是目标节点时:
current = 从 OPEN 中移除最低排名项
将 current 添加到 CLOSED
对于 current 的每个邻居节点:
cost = g(current) + movementcost(current, neighbor)
如果 neighbor 在 OPEN 中 且 cost 小于 g(neighbor):
从 OPEN 中移除 neighbor,因为找到了更好的路径
如果 neighbor 在 CLOSED 中 且 cost 小于 g(neighbor):
从 CLOSED 中移除 neighbor
如果 neighbor 不在 OPEN 也不在 CLOSED 中:
设置 g(neighbor) 为 cost
将 neighbor 添加到 OPEN
设置优先队列排名为 g(neighbor) + h(neighbor)
设置 neighbor 的父节点为 current在这个伪代码中,CLOSED列表的作用是防止算法重新处理已经评估过的节点。如果发现到达一个已在CLOSED列表中的节点有更优的路径,则需要将其从CLOSED中移除,并重新添加到OPEN中进行评估。这确保了即使某个节点曾被以次优路径访问过,如果后续发现了更优路径,也能得到更新。
立即学习“Python免费学习笔记(深入)”;
现在,我们来看一个实际的Python A算法实现,它在功能上实现了A算法,但并没有显式地使用CLOSED列表:
from pyamaze import maze,agent,textLabel
from queue import PriorityQueue
def h(cell1,cell2):
x1,y1=cell1
x2,y2=cell2
return abs(x1-x2) + abs(y1-y2)
def aStar(m):
start=(m.rows,m.cols)
g_score={cell:float('inf') for cell in m.grid}
g_score[start]=0
f_score={cell:float('inf') for cell in m.grid}
f_score[start]=h(start,(1,1))
open=PriorityQueue()
open.put((h(start,(1,1)),h(start,(1,1)),start)) # (f_score, h_score, cell)
aPath={}
while not open.empty():
currCell=open.get()[2] # 获取f_score最低的节点
if currCell==(1,1): # 目标节点
break
for d in 'ESNW': # 遍历邻居
if m.maze_map[currCell][d]==True: # 如果路径可达
# 计算邻居节点坐标
if d=='E': childCell=(currCell[0],currCell[1]+1)
if d=='W': childCell=(currCell[0],currCell[1]-1)
if d=='N': childCell=(currCell[0]-1,currCell[1])
if d=='S': childCell=(currCell[0]+1,currCell[1])
temp_g_score=g_score[currCell]+1 # 假设移动代价为1
temp_f_score=temp_g_score+h(childCell,(1,1))
# 核心逻辑:检查是否找到更优路径
if temp_f_score < f_score[childCell]:
g_score[childCell]= temp_g_score
f_score[childCell]= temp_f_score
open.put((temp_f_score,h(childCell,(1,1)),childCell)) # 将更新后的节点重新加入OPEN
aPath[childCell]=currCell
# 路径回溯
fwdPath={}
cell=(1,1)
while cell!=start:
fwdPath[aPath[cell]]=cell
cell=aPath[cell]
return fwdPath
if __name__=='__main__':
m=maze(5,5)
m.CreateMaze()
path=aStar(m)
a=agent(m,footprints=True)
m.tracePath({a:path})
l=textLabel(m,'A Star Path Length',len(path)+1)
m.run()此Python实现的关键在于它如何通过g_score和f_score字典来隐式管理节点状态,从而避免了显式的CLOSED列表。
初始化为无穷大: g_score和f_score字典中的所有节点最初都被初始化为float('inf')。这有效地将所有节点标记为“未访问”或“尚未找到有效路径”。
if temp_f_score < f_score[childCell] 条件: 这是核心所在。当算法探索一个childCell时,它会计算一条新的到达该节点的路径代价temp_f_score。
该Python实现之所以能够省略显式的CLOSED列表,原因在于以下几点:
f_score作为最佳路径记录:f_score[cell]始终存储着从起点到cell的已知最佳路径的f_score。当一个节点从open队列中取出时,它保证是当前所有待评估节点中f_score最低的。如果后续又通过其他路径发现了到达同一节点的更优路径(即temp_f_score < f_score[childCell]),那么f_score[childCell]会被更新,并且该节点会以新的、更低的f_score再次被放入open队列。
优先队列的特性:PriorityQueue会自动按照优先级(即f_score)排序。如果同一个节点因为不同的路径被多次放入open队列,只有具有最低f_score的那个实例会被首先取出处理。当后续取出具有较高f_score的同一节点实例时,由于f_score[childCell]已经被更新为更低的值,条件temp_f_score < f_score[childCell](此时temp_f_score是旧的、较高的值)将不再满足,因此这些冗余的、次优的路径实例会被自然地忽略,不会导致错误的路径更新。
避免重复处理的机制:虽然没有显式的CLOSED列表来防止节点被重复“扩展”,但if temp_f_score < f_score[childCell]这个条件有效地确保了只有当找到更优路径时,才更新节点信息并将其重新放入OPEN队列。对于已经以最优路径处理过的节点,任何后续发现的等效或更差的路径都不会导致其状态被更新,从而避免了不必要的重复计算。
理解这两种实现方式,有助于开发者在不同场景下选择最适合的A算法实现策略。核心在于理解A算法如何通过f_score来指导搜索,并有效地管理已访问节点的最佳路径信息。
以上就是A算法中的OPEN与CLOSED列表:Python实现与原理分析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号