迷宫寻路问题——A*算法
迷宫寻路问题是人工智能中的有趣问题,如何表示状态空间和搜索路径是寻路问题的重点,本文的主要内容是A*搜索算法的理解和应用,首先对基本知识和算法思想进行了解,再通过其对迷宫问题求解应用,编写 Python 程序进行深入学习。
1. 搜索区域
我们假设某个人要从 Start 点到达 End 点,存在墙壁把这两个点层层隔开,如下图所示,绿色部分代表起点 Start 和终点 End,红色部分代表它们之间的墙:
我们把这一块搜索区域分成了一个一个的方格,使搜索区域简单化,这正是寻找路径的第一步。这种方法将我们的搜索区域简化成了一个普通的二维数组。数组中的每一个元素表示对应的一个方格,该方格的状态被标记为可通过的和不可通过的。通过找出从 Start 点到 End 点所经过的方格,就能得到 Start->End 的路径。
2. Open 和 Closed 表
创建了一个简单的搜索区域后,A*算法有两个重要的数据列表:一个记录下所有被考虑来寻找最短路径的方格(称为 open列表)和一个记录下不会再被考虑的方格(称为 closed列表)。
首先在 closed 列表中添加起点位置,然后把所有与它当前位置相邻的可通行小方格添加到 open 列表中。在A*算法中,我们从起点开始,依次检查它的相邻方格,选取相邻方格然后继续向外扩展直到找到目的地。但是该选哪一个方格呢?我们需要一个评价值。
3. 路径评价
设置路径上的每个方格对应一个 评价值 F = G + H:
G 是从起点沿着已生成的路径到一个给定方格的移动开销,从起点开始到相邻方格的移动量为1,该值会随着离始点越来越远而增大。
H 是从当前方格到终点的移动估算值,被称为视探,因为我们并不能确定剩余移动开销是多少,它仅仅是一个估算值。移动量估算值离真实值越接近,最终的路径会更加精确。如果估计值停止作用,很可能生成的路径不会是最优的。
4. 算法流程
重复以下步骤,直到遍历到终点 End,找到最短路径:
选取当前 open 列表中评价值 F 最小的节点,将这个节点称为 S;
将 S 从 open 列表移除,然后添加 S 到 closed 列表中;
对于与 S 相邻的每一块可通行的方格节点 T:如果 T 在 closed 列表中,忽略;如果 T 不在 open 列表中,添加它然后计算出它的评价值 F;如果 T 已经在 open 列表中,当我们从 S 到达 T 时,检查是否能得到 更小的 F 值,如果是,更新它的 F 值和它的前继(parent = S)。
在此迷宫问题中,将使用 “曼哈顿距离”(也叫 “曼哈顿长” 或者 “城市街区距离” )作为 H 值,计算出距离终点水平和垂直方向上的方格数量和,忽略障碍物。这里 H 作为一种搜索的启发信息,搜索过程为广度优先搜索+贪心策略。当 End 被加入到 open 列表作为待检验节点时,表示路径被找到,此时应终止循环;或者当 open 列表为空,表明已无可以添加的新节点,而已检验过的节点中没有终点节点,则意味着无路径可达终点,此时也终止循环。从终点开始沿父节点回溯到起点,记录整个遍历中得到的节点坐标,便得到了最优路径。
5. 算法实现
输入地图:
基本的搜索流程:
# 查找路径的入口函数
def find_path(self):
# 构建开始节点
p = Node_Elem(None, self.s_x, self.s_y, 0.0)
while True:
# 扩展节点
self.extend_round(p)
# 如果open表为空,则不存在路径,返回
if not self.open:
return
# 取F值最小的节点
idx, p = self.get_best()
# 到达终点,生成路径,返回
if self.is_target(p):
self.make_path(p)
return
# 把此节点加入close表,并从open表里删除
self.close.append(p)
del self.open[idx]
选取 F 值最小的节点作为扩展节点,其中 G 是实际移动量,H 是估计剩余移动量,我们取用曼哈顿距离:
# 求距离
def get_dist(self, i):
# F = G + H
return i.dist + math.sqrt((self.e_x-i.x)*(self.e_x-i.x))+ math.sqrt((self.e_y-i.y)*(self.e_y-i.y))
当我们遍历到终点节点的时候,对路径进行回溯,就能得到最优路径:
# 生成路径
def make_path(self, p):
# 从终点回溯到起点,起点的parent为None
while p:
self.path.append((p.x, p.y))
p = p.parent
运行结果:
6. 总结
在本文的实例中,我们采用A*算法得到了最终路径,搜索过程为启发式广度优先搜索,节点选择上实质上为贪心算法。