A* Search Algorithm
前段时间我们设想了一个需求,想帮助用户规划一下从 A -> B 的航线。对于路径规划从来没弄过,研究了一下,基本都在提这个 A 星寻路算法。
先贴几个文章:
- 简单的讲解的文章例如 https://www.jianshu.com/p/65282bd32391
- 这个详细一点的 https://blog.csdn.net/DinnerHowe/article/details/79380317
我写了一个简单的程序,这个程序没有做过任何的优化,只能说是解释了这个算法的逻辑而已,在终端里面可以可视化的把计算过程显示出来。效果可以看这里。
#!/usr/bin/python
import sys
import random
RED = '\033[31m'
GREEN = '\033[32m'
GRAY = '\033[35m'
NC = '\033[0m'
class Point(object):
x = 0
y = 0
close = False
open = False
start = False
end = False
wall = False
H = 99
G = 99
parent = None
def __init__(self, **kwargs):
if 'x' in kwargs:
self.x = kwargs['x']
if 'y' in kwargs:
self.y = kwargs['y']
self.key = '{}-{}'.format(self.x, self.y)
def __str__(self):
return '{},H{:2},G{:2},F{:2},P{:3}'.format(
self.key,
self.H if self.H != 99 else '',
self.G if self.G != 99 else '',
self.G + self.H if self.G != 99 else '',
self.parent.key if self.parent else '')
def __lt__(self, other):
return self.x < other.x if self.y == other.y else self.y < other.y
def get_area(width, height):
area = {}
for i in range(width):
for j in range(height):
point = Point(x=i, y=j)
area[point.key] = point
return area
def show_result(area_hash):
prev_y = -1
for point in sorted(area_hash.values()):
if ( prev_y != point.y ):
if (prev_y != -1):
print('')
prev_y = point.y
if point.start:
format = RED + 'S' + NC
elif point.end:
format = RED + 'E' + NC
elif point.wall:
format = GRAY + 'W' + NC
elif point.close:
format = GREEN + '0' + NC
else:
format = ' '
print((format + '({}) ').format(point), end='')
print('')
def set_preset(area, start, end, wall):
area[start].start = True
area[end].end = True
for key in wall:
area[key].wall = True
return area
def is_valid_point(point, width, height):
if point.wall or point.close:
return False
if point.x < 0 or point.x > width:
return False
if point.y < 0 or point.y > height:
return False
return True
def get_around(area, point, width, height):
n = '{}-{}'.format(point.x, point.y - 1)
s = '{}-{}'.format(point.x, point.y + 1)
l = '{}-{}'.format(point.x - 1, point.y)
r = '{}-{}'.format(point.x + 1, point.y)
res = {}
for key in [n, s, l, r]:
if key in area and is_valid_point(area[key], width, height):
area[key].parent = point
area[key].G = area[key].G if area[key].G != 99 else (point.G if point.G != 99 else 0) + 1
res[key] = area[key]
return res
def get_H(point, end_point):
return abs(point.x - end_point.x) + abs(point.y - end_point.y)
step = 0
opened = {}
def go(area, start, end, width, height):
global step, opened
start_point = area[start]
end_point= area[end]
start_point.close = True
if start_point.key in opened:
del opened[start_point.key]
points = get_around(area, start_point, width, height)
opened.update(points)
rnd = random.choice(list(opened))
next_point = area[rnd]
for key, point in opened.items():
point.H = get_H(point, end_point)
if point.H + point.G <= next_point.H + next_point.G:
next_point = point
print('step {}: {}'.format(step, next_point))
step += 1
if step >= 3:
pass
if next_point and next_point.key != end:
go(area, next_point.key, end, width, height)
def get_result(area, start, end):
print('{}{}{} <- '.format(RED, area[end].key, NC), end='')
parent = area[end].parent
if(parent.key != start):
get_result(area, start, parent.key)
else:
print('{}{}{}'.format(RED, area[start].key, NC), end='')
def main():
width = 10
height = 10
start = '1-3'
end = '8-4'
wall = ['2-2', '3-2', '3-3', '3-4', '3-5', '3-6', '5-4', '2-6', '1-6']
area = get_area(width, height)
area = set_preset(area, start, end, wall)
go(area, start, end, width, height)
show_result(area)
get_result(area, start, end)
if __name__ == '__main__':
main()
这个算法通过调整 G 和 H 的计算逻辑,可以平衡寻路速度和路径是否最短。另外,对于 open 列表的维护,也可以使用一些适合自己的数据结构来得到比较快速的查找。上面第二个帖子里面比较详细的列了一些改进算法的思路。