本系列旨在用简单的人话讲解算法,尽可能避免晦涩的定义,读者可以短时间内理解算法原理及应用细节。我在努力!
本篇文章编程语言为Python,供参考。
A*算法
一种静态路网中求解最短路径最有效的直接搜索方法。(百度百科)
算法基本思路:创建两个数组分别记录待访问、已访问的结点,先将起始点放入待访问的数组,每次从待访问数组中选取F值最小的一个结点,然后将其上下左右四边中有效的结点放入待访问数组(有效条件:不出界、无障碍、未访问),不断重复以上操作,直到终点出现在待访问数组中(存在最短路径)或待排序数组为空(无法到达)为止。
附全部源码:
brickList = [
[0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0],
[0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0],
[1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1],
[0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0],
[1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1],
[0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0],
]
#分别求当前迷宫的宽度及高度
w_brilist = len(brickList[0])
h_brilist = len(brickList)
zongzipos = (9,4)
class Grid():
def __init__(self, x, y, dest=None, parent=None):
#当前点的横纵坐标
self.x = x
self.y = y
#目标结点(用于计算H值)
self.dest = dest
#用于记录当前结点的前驱结点(输出路径用)
self.parent = parent
if dest is not None:
#G值为从起始点到当前点的距离
self.g = self.parent.g + 1 if self.parent is not None else 1
#H值为从当前点到终点的曼哈顿距离(不考虑障碍)
self.h = abs(dest.x - self.x) + abs(dest.y - self.y)
#F值为从起始点经过当前点到终点的距离
self.f = self.g + self.h
def get_valid_neighbors(self, open_list, close_list):
#从上下左右四个方向判断邻居结点是否有效
a = [(1,0), (-1,0), (0,1), (0,-1)]
#用于保存有效的邻居结点
res = []
#逐个判断四边的邻居结点是否有效
for i in a:
#创建并判断邻居结点是否有效,有效保存
tmp = Grid(self.x+i[0], self.y+i[1], self.dest, self)
if tmp.is_valid(open_list, close_list):
res.append(tmp)
return res
def is_valid(self, open_list, close_list):
#出界
if not (0 <= self.x < w_brilist and 0 <= self.y < h_brilist):
return False
#遇障碍物
if brickList[self.x][self.y] != 0:
return False
#是否已出现过
for i in open_list + close_list:
if self.x == i.x and self.y == i.y:
return False
return True
def is_dest(self):
#判断是否为终点结点
return self.x == self.dest.x and self.y == self.dest.y
def a_star_algorithm(src, dest):
#初始化待访问及已访问列表
open_list = [src]
close_list = []
#每次选取F值最小的一个结点,将他所有有效邻居加入列表,直到列表为空或出现终点为止。
while open_list != []:
#获取f值最小节点
open_list.sort(key=lambda x: x.f)
now = open_list.pop(0)
close_list.append(now)
#向待访问列表中加入当前结点所有有效邻居结点
open_list.extend(now.get_valid_neighbors(open_list, close_list))
#判断终点是否出现在待访问列表
for i in open_list:
if i.is_dest():
return i
return None
def get_path(dest):
path = []
while dest is not None:
path.append((dest.x,dest.y))
dest = dest.parent
return path[::-1]
dest = Grid(*zongzipos)
src = Grid(0, 0, dest, None)
c = a_star_algorithm(src, dest)
path = get_path(c)
print(path)
成果演示视频:
(为演示清楚,用pygame做了个图像版)
参考资料:《漫画算法:小灰的算法之旅(Python篇)》
后记:很有意思的一个算法,尤其是能用pygame库展示出来看效果比直接看一串坐标清楚地多。