A 星路径搜索 算法
- 算法描述
用白话讲,这个过程就是 从A点发出的光在经过很多 蜿蜒曲折的过程,最终有一个最先到达的光束。这个光束就是最短路径信息;
生活中见过小叶榕树吧?每每下雨之后就会长出很多根须。如果类比就是从某一点 长出了很多很多的根须,他的目标是到达土里进行养分汲取。 在这个过程中可能会遇到墙就只有转弯等绕过;其中最先到达地面的就是一条最短路径
PS:
上述2个描述,为个人总结。为便于理解所构思的内容
数据结构描述
开辟openlist closelist 用于记录;
openlist 表示 当前可以访问到的节点,其中节点已经计算出F
closelist 表示 搜索路径中已经到过的节点从某一节点出发,分别 上 下 左 右 左上 右上 左下 右下 共8个位置可以进行探索。就类比与九宫格从中心点出发到各个点所需花费的路径为 (10-上 下 左 右) (14 左上 右上 左下 右下)10 和 14 是为方便计算和表示的一个近似值,准确的应该是 1 和 根号2(约等于1.4…) 的值 。这个从某一节点出发的值 称为 G 值
某一节点 X 距离 终点 可以横冲直撞所需的最小 花费值为 H 值。这里的横冲直撞就是不考虑所经过的节点是否是不能通过等其它限制因素。通常 H 值为 节点X到终点的 坐标差值的绝对值.例如:X(1,2) 终点 (8,8) 则 H值为 H = abs(8-1) + abs(8-2) = 7+6=13
某一点的所需要耗费的 总值F 等于 G+H 的数值,若某一点的F值很小说明从起点出发到该点的花费很小反之则大。我们应选择F值小的节点进行探索
程序执行步骤
-
A . 将起点加入到openlist中
-
B. 若openlist 中存在节点,则取出openlist中F值最小的一个节点M 开始探索
-
C. 若取出该节点M为终点则跳出 循环,处理路径信息
-
D. 开始从该节点M探索该节点M可达周围节点进行 探索;
a. 计算节点M到达探索节点所需耗费的F值信息
b.
b1. 设置探索节点的父节点为 M,并将该节点 添加到openlist中去
b2. 若该探索的节点已经存在openlist中时,若该节点的F值小于 当前探索的F值。则更新F值为旧有的F值,并设置探索节点的父节点为 M
-
E.开始从B步骤循环执行
循环结束后打印路径信息
-
F. 溯源 路径信息,从终点追溯父节点信息。就是到达终点所需的最小路径信息
下饭菜,即将登场 A星算法(python版)
# 如下 标记,为一个py文件分隔。
#------------------------------------------------------------------------
# 坐标
class Coord:
def __init__(self,x,y):
self.x = x
self.y = y
def equals(self,someone):
if(someone is not None):
# 不为空
if(self.x == someone.x and self.y == someone.y):
return True
else:
return False
else:
return False
#------------------------------------------------------------------------
from Coord import Coord
# 节点
class Node:
def __init__(self,coord,parent,G,H):
self.coord = coord
self.parent = parent
self.G = G
self.H = H
def __init2__(self,x,y):
self.coord = Coord(x,y)
self.G = 0
self.H = 0
def toString(self):
aStr = '{x=' + str(self.coord.x)+',y='+str(self.coord.y)+';F='+str(self.G+self.H)+'G='+str(self.G)+'H='+str(self.H)+'}'
return aStr
def toString2(self):
aStr = '{x=' + str(self.coord.x)+',y='+str(self.coord.y)+'};'
return aStr
#-----------------------------------------------------------------------------
# 描述一个待搜索的矩阵(二维数组)信息
class MapInfo:
def __init__(self,maps,width,height,start,end):
self.maps = maps
self.width = width
self.height = height
self.start = start
self.end = end
#-----------------------------------------------------------------------------
from Coord import Coord
from Node import Node
# A星算法
class Astar:
BAR = 1 # 障碍值
_PATH = 2 # 路径信息
DIRECT_VALUE = 10 # 横竖移动代价
OBLIQUE_VALUE = 14 # 斜移动代价
openlist = [] # openglist
closelist = [] # clsoelist
closemap = {} # 记录已经探索过的节点信息,方便打印探索过程信息
def __init__(self):
return
def start(self,mapInfo):
self.openlist = []
self.closelist = []
self.closemap = {}
# 加入起点到openlist
self.openlist.append(mapInfo.start)
# 开始探索
self.moveNodes(mapInfo)
def moveNodes(self,mapInfo):
steps = 1 # 步数
while (len(self.openlist) > 0):
currentNode = self.openlist.pop(0) # 由于openlist 已经排好序了,直接取出第一个 即为F值最小的节点
# if(currentNode.coord.x==5 and currentNode.coord.y==5):
# print("温馨提示:==============================================到达终点了哦")
print(steps,"次从", currentNode.coord.x,currentNode.coord.y,"节点,探索周围")
self.closelist.append(currentNode)
# 获取map key信息
key = self.get_map_key2(currentNode.coord.x,currentNode.coord.y)
self.closemap[key] = currentNode
# 探索周围
self.add_neighbor_node_in_open(mapInfo,currentNode)
self.debug_print_proccess_info(mapInfo)
steps = steps+1
if(self.is_coord_in_close(mapInfo.end.coord)):
# print("标记路径---")
self.draw_path(mapInfo.maps,mapInfo.end)
break
#标记 路径信息
def draw_path(self,maps,end):
if(maps is None or end is None):
return
print('总代价为',end.G)
while (end is not None and end.coord is not None):
coord = end.coord
maps[coord.y][coord.x] = self._PATH
end = end.parent
# print('=========================',maps)
# node是否在closelist中
def is_coord_in_close(self,coord):
# print("查找是否在close中",coord.x,coord.y)
if(self.closelist is not None):
_length = len(self.closelist)
for i in range(0,_length):
node = self.closelist[i]
# print(node.coord.x,node.coord.y)
if(node.coord.x == coord.x and node.coord.y == coord.y):
# print("=====在close中")
return True
# print("222不在close中")
return False
else:
# print("不在close中")
return False
def add_neighbor_node_in_open(self,mapInfo , current):
x = current.coord.x
y = current.coord.y
# print("探索", x,y,"节点")
# 左
self.add_one_node_in_open(mapInfo,current,x-1,y,self.DIRECT_VALUE)
# 右
self.add_one_node_in_open(mapInfo,current,x+1,y,self.DIRECT_VALUE)
# 上
self.add_one_node_in_open(mapInfo,current,x,y+1,self.DIRECT_VALUE)
# 下
self.add_one_node_in_open(mapInfo,current,x,y-1,self.DIRECT_VALUE)
# 左上
self.add_one_node_in_open(mapInfo,current,x-1,y-1,self.OBLIQUE_VALUE)
# 左下
self.add_one_node_in_open(mapInfo,current,x-1,y+1,self.OBLIQUE_VALUE)
# 右上
self.add_one_node_in_open(mapInfo,current,x+1,y+1,self.OBLIQUE_VALUE)
# 右下
self.add_one_node_in_open(mapInfo,current,x+1,y-1,self.OBLIQUE_VALUE)
def add_one_node_in_open(self,mapInfo , current, x, y , costV):
if(self.can_add_to_open(mapInfo,x,y)):
# print(x,y)
end_node = mapInfo.end
coord = Coord(x,y)
temp_G = current.G + costV
child = self.find_node_in_open(coord)
if(child is not None): # 已经存在open中时
if(child.G > temp_G):
child.G = temp_G
child.parent = current
self.openlist.append(child)
else: # 没有在open中找到
# 计算H值
temp_H = self.calcH(end_node.coord,coord)
if(self.is_end_node(end_node.coord,coord)):
child = end_node
child.parent = current
child.G = temp_G
child.H = temp_H
else:
child = Node(coord,current,temp_G,temp_H)
self.openlist.append(child)
# 给openlist 排个序
self.sort_open_list()
def sort_open_list(self):
self.openlist.sort(key=self.getComparteValue, reverse=False)
# self.debug_open_list()
def debug_print_proccess_info(self,mapInfo):
maps = mapInfo.maps
y_length = len(maps[0])
x_length = len(maps)
for x in range(0,x_length):
a_row = ""
for y in range(0,y_length):
item = maps[x][y]
if(item == self.BAR):
a_row = a_row + "#" + " "
continue
key = str(y) + "__" + str(x)
# print(self.closemap)
obj_in_map = self.closemap.get(key)
is_in_map = obj_in_map is not None
str_item = ""
if(is_in_map):
str_item = "@ "
else:
str_item = "- "
a_row = a_row + str_item
print(a_row)
print("=============================================")
return
def debug_open_list(self):
_length = len(self.openlist)
aStr = ''
for i in range(0,_length):
item = self.openlist[i]
#print(item,item.coord.x,item.coord.y,item.G,item.H)
aStr = aStr + item.toString2() + " "
print(aStr)
def getComparteValue(self,item):
_F = item.G + item.H
return _F
def is_end_node(self,end,coord):
return (end is not None and end.equals(coord))
# 计算 H值
def calcH(self,end,coord):
_x = abs(end.x - coord.x)
_y = abs(end.y - coord.y)
return _x + _y
# 查找open中的信息
def find_node_in_open(self,coord):
if(coord is None or self.openlist is None):
return None
_length = len(self.openlist)
for i in range(0,_length):
node = self.openlist[i]
if(node.coord.equals(coord)):
return node
return None
# 是否能加入到openlist中去
def can_add_to_open(self,mapInfo, x, y):
if(x < 0 or x >= mapInfo.width or y < 0 or y >= mapInfo.height):
return False
#if(mapInfo.maps[x][y] == self.BAR):
if(mapInfo.maps[y][x] == self.BAR):
return False
if(self.is_in_close_list(x,y)):
return False
return True
# 是否在 close 的list中已经存在
def is_in_close_list(self,x,y):
# if(self.closelist is None):
# return False
_length = len(self.closelist)
if(_length <= 0):
return False
for i in range(0, _length):
node_item = self.closelist[i]
if(node_item.coord.x == x and node_item.coord.y == y):
return True
return False
def get_map_key(self,node):
aStr = str(node.coord.x) + "__" + str(node.coord.y)
return aStr
def get_map_key2(self,x,y):
aStr = str(x) + "__" + str(y)
return aStr
#-----------------------------------------------------------------------------
from MapInfo import MapInfo
from Astar import Astar
from Node import Node
from Coord import Coord
def printMap(maps):
# print(maps)
y_length = len(maps[0])
x_length = len(maps)
for x in range(0,x_length):
a_row = ""
for y in range(0,y_length):
item = maps[x][y]
a_row = a_row + str(item) + " "
print(a_row)
# 调用A星算法 入口
if __name__ == '__main__':
maps = [
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 ],
[ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 ],
[ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ]
]
start_coord = Coord(1,1)
start = Node(start_coord,None,0,0)
end_coord = Coord(5,5)
end = Node(end_coord,None,0,0)
info = MapInfo(maps,len(maps[0]), len(maps),start, end)
a_start = Astar()
a_start.start(info)
printMap(maps)
运行结果
1 次从 1 1 节点,探索周围
- - - - - - - - - - - - - - -
- @ - - - - - - - - - - - - -
- - # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
2 次从 2 1 节点,探索周围
- - - - - - - - - - - - - - -
- @ @ - - - - - - - - - - - -
- - # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
3 次从 1 2 节点,探索周围
- - - - - - - - - - - - - - -
- @ @ - - - - - - - - - - - -
- @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
4 次从 0 1 节点,探索周围
- - - - - - - - - - - - - - -
@ @ @ - - - - - - - - - - - -
- @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
5 次从 1 0 节点,探索周围
- @ - - - - - - - - - - - - -
@ @ @ - - - - - - - - - - - -
- @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
6 次从 0 2 节点,探索周围
- @ - - - - - - - - - - - - -
@ @ @ - - - - - - - - - - - -
@ @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
7 次从 2 0 节点,探索周围
- @ @ - - - - - - - - - - - -
@ @ @ - - - - - - - - - - - -
@ @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
8 次从 0 0 节点,探索周围
@ @ @ - - - - - - - - - - - -
@ @ @ - - - - - - - - - - - -
@ @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
9 次从 3 1 节点,探索周围
@ @ @ - - - - - - - - - - - -
@ @ @ @ - - - - - - - - - - -
@ @ # # - - - - - - # # # - -
- - - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
10 次从 1 3 节点,探索周围
@ @ @ - - - - - - - - - - - -
@ @ @ @ - - - - - - - - - - -
@ @ # # - - - - - - # # # - -
- @ - # - - - - - # # - - - -
- - - # # # # # # # - - - - -
- - - # - - # - - - - - - - -
- - - # - - - - # - - - - - -
=============================================
...
80 次从 7 5 节点,探索周围
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ # # @ @ @ @ @ @ # # # @ @
@ @ @ # @ @ @ @ @ # # @ @ @ @
@ @ @ # # # # # # # @ @ @ @ @
@ @ @ # - - # @ @ @ @ @ @ @ @
@ @ @ # - - - - # @ @ @ @ @ @
=============================================
81 次从 7 6 节点,探索周围
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ # # @ @ @ @ @ @ # # # @ @
@ @ @ # @ @ @ @ @ # # @ @ @ @
@ @ @ # # # # # # # @ @ @ @ @
@ @ @ # - - # @ @ @ @ @ @ @ @
@ @ @ # - - - @ # @ @ @ @ @ @
=============================================
82 次从 6 6 节点,探索周围
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ # # @ @ @ @ @ @ # # # @ @
@ @ @ # @ @ @ @ @ # # @ @ @ @
@ @ @ # # # # # # # @ @ @ @ @
@ @ @ # - - # @ @ @ @ @ @ @ @
@ @ @ # - - @ @ # @ @ @ @ @ @
=============================================
83 次从 5 6 节点,探索周围
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ # # @ @ @ @ @ @ # # # @ @
@ @ @ # @ @ @ @ @ # # @ @ @ @
@ @ @ # # # # # # # @ @ @ @ @
@ @ @ # - - # @ @ @ @ @ @ @ @
@ @ @ # - @ @ @ # @ @ @ @ @ @
=============================================
84 次从 5 5 节点,探索周围
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ # # @ @ @ @ @ @ # # # @ @
@ @ @ # @ @ @ @ @ # # @ @ @ @
@ @ @ # # # # # # # @ @ @ @ @
@ @ @ # - @ # @ @ @ @ @ @ @ @
@ @ @ # - @ @ @ # @ @ @ @ @ @
=============================================
总代价为 224
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 2 2 2 2 2 2 2 2 2 2 2 2 0 0
0 0 1 1 0 0 0 0 0 0 1 1 1 2 0
0 0 0 1 0 0 0 0 0 1 1 2 2 0 0
0 0 0 1 1 1 1 1 1 1 2 0 0 0 0
0 0 0 1 0 2 1 2 2 2 0 0 0 0 0
0 0 0 1 0 0 2 0 1 0 0 0 0 0 0
片尾
笔者能力不足,本来很简单的算法。花了3天时间,才吃透 并结合java版本并将算法用python实现。
在自己写代码的过程中,也出现了bug。在调试时吃了 不细心的亏。望各位同僚切不可自大若有想要java版的,请留言私信