在记录前先将A*搜索算法进行一下记录及讲解(是广度优先搜索和深度优先搜索的续写),个人学习理解及程序实现笔记(若存在错误请理解)。在本篇学习笔记中记录A*算法是因为下周老师要抽他们起来讲解一下我写的这代码(哈哈哈),所以在这做个笔记给下周的俩位幸运儿讲解一下下。
1 A*算法
1.1 A*算法对比广度优先算法和深度优先算法
在前面的一篇广度优先算法和深度优先算法中对它们两实现的主要过程和实现的程序进行了一下说明解释,在这为与A*算法对再记录一下,如果迷糊可以看上一篇文章 ↓。
上一篇文章链接:深度优先搜索(DFS)和广度优先搜索(BFS),用代码讲原理-CSDN博客
广度优先搜索算法:实现的过程是才用队列的形式,及先进先出(指进入队列的可移动的下一个节点),简单来说我们在程序里面就去边界(路径节点存放)列表的第一个节点及list[0],在一个节点上如果存在两个及两个以上的下一个可行动节点,那么如何行动就是通过自己在程序中设定的形式来进行(及走上、下、左、右看谁先进入队列)。不理解看上一篇文章内容(虽然可能上一篇也没说的很清楚哈哈哈)
深度优先搜索算法:深度优先与广度优先不同他是做的堆栈,先进后出的方法来进行路径搜索,就是取边界(路径节点存放)列表的最后一个节点及list[-1]。
A*算法:这章主题,与前两个不同,A*算法在前两个的基础上加了两个路径(需要保持在节点当中),一个是从起始点到当前节点行走的路径长度g(x),一个是当前节点到目标点的距离h(x)(这个距离计算采用曼哈顿公式),两者加起来得到一个启发式距离:
A*算法在边界(路径存放节点)中选取下一个节点的方式就是找到启发式距离最小的那一个,及min(list)
1.2 A*算法的具体实现过程
因为我们是通过在上一篇代码中进行添加修改,所以我们需要明确实现A*搜索的必要条件:
- 我们每走到一个节点需要得到当前节点的g(x)(起始点到当前节点的距离)和启发式距离D
- 我们需要在存放路径节点的边界列表中取到启发式距离最近的一个节点
明确要求,开始编写程序:
Node类的改写如下:
class Node():
def __init__(self, state, parent, action,distence,action_dis):
self.state = state
self.parent = parent
self.action = action
# 下面两个数据是我们新加的
self.a_distence = distence
self.action_dis = action_dis
我们在原本的对象(节点)中添加了两个形参和两个变量,它们分别表示起始点到当前节点的路径距离和当前节点的启发式距离
A*算法的实现:
class A_star(StackFrontier):
def remove(self):
if self.empty():
raise Exception('empty frontier')
else:
# 因为我们起始点的距离是None所以单独进行了一次判断
if (i.a_distence for i in self.frontier)==None:
node = self.frontier[0]
self.frontier = self.frontier[1:]
print(self.frontier)
return node
else:
# 找到边界列表中启发式距离最近的节点的索引值
min_dis = list(i.a_distence for i in self.frontier).index(min(i.a_distence for i in self.frontier))
# 获取该节点并从边界列表中删除该节点
node = self.frontier[min_dis]
self.frontier.pop(min_dis)
return node
实现过程与广度优先搜索和深度优先搜索差不多,只不过我们多进行了一次判断和对最小启发式距离的索引值进行获取,其它的是一样的。
主程序的更改:
# 源代码第167行
start = Node(state=self.start, parent=None, action=None,distence=None,action_dis=0)
print("起始点的坐标",start.state)
frontier = A_star()
frontier.add(start)
self.distence = 0
因为我们更改了Node对象,所以选哟对起始点的Node参数进行补全。将frontier缓存我们写的A_star类。并添加了从起始点出发的运动距离,然后往下的循环里进行更改self.distence:
while True:
# If nothing left in frontier, then no path
if frontier.empty():
raise Exception("no solution")
# Choose a node from the frontier
# r = QueueFrontier()
node = frontier.remove()
self.num_explored += 1
# 更改我们的距离 起始点到当前节点的距离加上1,因为这个距离是当前节点的下一个节点的距离
self.distence = node.action_dis + 1
# If node is the goal, then we have a solution
if node.state == self.goal:
for action, state in self.neighbors(node.state):
if not frontier.contains_state(state) and state not in self.explored:
# state 和 action保存的是子节点的行动和状态,而parent保存了父节点的所有状态,比如从起始点到第二个点,parent保存的就是起始状态的所有状态
distence_a = abs(state[0]-self.goal[0]) + abs(state[1]-self.goal[1]) + self.distence
child = Node(state=state, parent=node, action=action,distence=distence_a,action_dis=self.distence)
frontier.add(child)
同样的在源代码的第210行的循环中对每个节点的启发式距离进行计算并对子节点的参数进行补全。整体的实现过程就完成了
看效果:
1.3 咱那看不懂的笔记
2 数据结构
明天写