学习A*寻路算法
A*
寻路
背景介绍
我们日常生活中的很多决策问题都可以转化为对应的路径规划问题,如何从复杂的路径中寻找两个或者多个点之间的最短距离就是一个很值得研究的东西
A*
算法是一种寻路算法,解决的是路径规划问题的一种
目的是:
找出某地图中两点之间的最短路径
寻路过程
先来看个地图
s:起点 e:终点 #:障碍
| | | | | | | |
| | | |#| | | |
| |s| |#| |e| |
| | | |#| | | |
| | | | | | | |
现在有这样的需求,我们需要找出s->e的最短路径 ,但是,s和e之间有一堵’墙’,就是”#”啦。这样怎么才能找出最短路径呢?
第一种做法(Dijkstra算法):
s:起点 e:终点 *:检索路径 #:障碍
|*|*|*| | | | |
|*|*|*|#| | | |
|*|s|*|#| |e| |
|*|*|*|#|*|*| |
|*|*|*|*|*| | |
或者我们可以这样做(A*):
s:起点 e:终点 *:检索路径 #:障碍
| | | | | | | |
|*|*|*|#| | | |
|*|s|*|#| |e| |
|*|*|*|#|*| | |
| | |*|*|*| | |
很明显,第二种做法比第一种做法检索的路径要少很多,当然,速度上就要快一些,对一些更复杂的地图,A*
要比普通广度优先的算法快上许多
例如:
s:起点 e:终点 *:检索路径 #:障碍
| | | | | | | | | | | | | |
| | | | | | | | |e| | | | |
| | | |#|#|#|#|#|#|#| | | |
| | |#| | | | | | | |#| | |
| | |#| | | | | | | |#| | |
| | |#| | | | | | | |#| | |
| | |#| | | | | | | |#| | |
| | | | | | | | | | | | | |
| | | | | | |s| | | | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
这个地图中从s到e,如果使用普通寻路可能是这样的
s:起点 e:终点 *:最终路径 #:障碍
| | | | | | | | | | | | | |
| | | | | | | | |e|*|*| | |
| | | |#|#|#|#|#|#|#|*|*| |
| | |#| | | |*|*|*|*|#|*| |
| | |#| | | |*| | |*|#|*| |
| | |#| | | |*| | |*|#|*| |
| | |#| | | |*| | |*|#|*| |
| | | | | | |*| | | |*|*| |
| | | | | | |s| | | | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
很明显,寻路过程中出现了较多的无用步数,寻路进入了布袋口里面,我们期望的是寻路尽量快速,寻找最短路径,A*
的做法就比较符合我们的期望
A*
寻路:
s:起点 e:终点 *:最终路径 #:障碍
| | | | | | | | | | | | | |
| | | | | | | | |e|*|*| | |
| | | |#|#|#|#|#|#|#|*|*| |
| | |#| | | | | | | |#|*| |
| | |#| | | | | | | |#|*| |
| | |#| | | | | | | |#|*| |
| | |#| | | | | | | |#|*| |
| | | | | | |*|*|*|*|*|*| |
| | | | | | |s| | | | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
A*
寻路的主要过程
A*
寻路的精髓
核心公式:$$F = G + H$$
F:用来评价点距起点距离的值
G:实际已耗费的路径
H:未来还需要耗费的路径
具体过程
- 将起点S加入到开放列表(开放列表中的点都是可用的)
- 寻找起点S周围所有可到达的点,并计算F值(最终衡量路径距离的值),设置这些点的父节点为起点,将这些点加入到一个开放列表
- 将起点从开放列表中删除,并添加到关闭列表,从开放列表中选取F值最小的点M(如果有多个最小F值,随机取其一)
- 将M从开放列表中删除,并添加到关闭列表,计算M周围可到达的点,重新计算F值,将周围这些点都存入开放列表,设置他们的父节点为M
- 如果M周围某个点已经在开放列表中,重新计算F值和G值(已消耗路径),选取新旧G值中最小的点
- 最后如果发现终点在开放列表中,终止程序
实质
A*
算法的实质是通过维护一个待检测点的列表(open list)
和一个已检测点的列表(closed list)
来记录寻路过程,从图形上来看,OPEN集是已访问区域的边界,CLOSED集是已访问区域的内部。每个节点还包含一个指向父节点的指针,以确定追踪关系。
算法有一个主循环,重复地从OPEN集中取最优节点n(即f值最小的节点)来检测.如果n是目标节点,那么算法结束;
否则,将节点n从OPEN集删除,并添加到CLOSED集中,然后查看n的所有邻节点n’,如果邻节点在CLOSED集,它已被检测过,则无需再检测;
如果邻节点在OPEN集,它将会被检测,则无需此时检测;
否则,将该邻节点加入OPEN集,设置其父节点为n,到n’的路径开销F(n') = G(n) + H(n,n')
A*
的变种
A*
算法可以通过动态加权
,跳跃点搜索
,双向搜索
,迭代深化
等进行性能和功能的优化
此外还有动态A*
与终身规划A*
等变化,这些变化其实都是基于基本的A*
理论来进行优化
寻路算法的应用
比较常见的是寻路算法在游戏中的应用,因为很多游戏中会有地图的概念,涉及到地图就很容易有寻路规划的需要
比如:许多RPG游戏中会有去寻找某个NPC接任务或者去某个地方打败某个BOSS
寻路算法在生活中也有应用,比如 探索和侦查,道路建设,地形分析,城市规划等
总之,深入研究寻路算法是非常有意义的
推荐阅读
如果有希望深入了解A*
的读者,强烈推荐一篇文章,作者写的非常用心,讲的也特别细
地址:http://theory.stanford.edu/~amitp/GameProgramming/index.html
代码演示
下面是一个Python实现的A*寻路
的代码:
1 |
|
主要程序
1 |
|