在学习A*算法以前,很好奇的是A*为何叫作A*。在知乎上找到一个回答,大体意思是说,在A*算法以前有一种基于启发式探索的方法来提升Dijkstra算法的速度,这个算法叫作A1。后来的改进算法被称为A*。*这个符号是从统计文献中借鉴来的,用来表示相对一个旧有标准的最优估计。git
启发式探索是利用问题拥有的启发信息来引导搜索,达到减小探索范围,下降问题复杂度的目的。github
A*寻路算法就是启发式探索的一个典型实践,在寻路的过程当中,给每一个节点绑定了一个估计值(即启发式),在对节点的遍历过程当中是采起估计值优先原则,估计值更优的节点会被优先遍历。因此估计函数的定义十分重要,显著影响算法效率。算法
将待搜索的区域简化成一个个小方格,最终找到的路径就是一些小方格的组合。固然是能够划分红任意形状,甚至是精确到每个像素点,这彻底取决于你的游戏的需求。通常状况下划分红方格就能够知足咱们的需求,同时也便于计算。
以下图区域,被简化成6*6的小方格。其中绿色表示起点,红色表示终点,黑色表示路障,不能通行。
缓存
先描述A*算法的大体过程:函数
初始节点,目标节点,分别表示路径的起点和终点,至关于上图的绿色节点和红色节点
F值,就是前面提到的启发式,每一个节点都会被绑定一个F值
F值是一个估计值,用F(n) = G(n) + H(n) 表示,其中G(n)表示由起点到节点n的固定消耗,H(n)表示节点n到终点的估计消耗。H(n)的计算方式有不少种,好比曼哈顿H(n) = x + y,或者欧几里得式H(n) = sqrt(x^2 + y^2)。本例中采用曼哈顿式。
F(n)就表示由起点通过n节点到达终点的总消耗
为了便于描述,本文在每一个方格的左下角标注数字表示G(n),右下角数字表示H(n),左上方数字表示F(n)。具体如何计算请看下面的一个例子学习
接下来,咱们严格按照A*算法找出从绿色节点到红色节点的最佳路径
首先将绿色节点加入到open列表中
接着判断open列表不为空(有起始节点),红色节点不在open列表中
而后从open列表中取出F值最小的节点,此时,open列表中只有绿色节点,因此将绿色节点取出,做为当前节点,并将其加入到close列表中
计算绿色节点的相邻节点(暂不考虑斜方向移动),以下图所示的全部灰色节点,并计算它们的F值。这些子节点既没有在open列表中,也没有在close列表中,因此都加入到open列表中,并设置它们的父节点为绿色节点优化
F值计算方式:
以绿色节点右边的灰色节点为例
G(n) = 1,从绿色节点移动到该节点,都只须要消耗1步
H(n) = 3,其移动到红色节点须要消耗横向2步,竖向一步,因此共消耗3步(曼哈顿式)
F(n) = 4 = G(n) + H(n)lua
试着算一下其余灰色节点的F值吧,看看与图上标注的是否一致3d
继续选择open列表中F值最小的节点,此时最小节点有两个,都为4。这种状况下选取哪个都是同样的,不会影响搜索算法的效率。由于启发式相同。这个例子中按照右下左上的顺序选取(这样能够少画几张图(T▽T))。先选择绿色节点右边的节点为当前节点,并将其加入close列表。其相邻4个节点中,有1个是黑色节点不可达,绿色节点已经被加入close列表,还剩下上下两个相邻节点,分别计算其F值,并设置他们的父节点为黄色节点。blog
此时open列表中F值最小为4,继续选取下方节点,计算其相邻节点。其右侧是黑色节点,上方1号节点在close列表。下方节点是新扩展的。主要来看下左侧节点,它已经在open列表中了。根据算法咱们要从新计算它的F值,按通过2号节点计算H(n) = 3,G(n)不变,因此F(n) = 6相比于原值反而变大了,因此什么也不作。(后面的步骤中从新计算F值都不会更小,再也不赘述)
此时open列表中F值最小仍为4,继续选取
此时open列表中F值最小为6,优先选取下方节点
此时open列表中F值最小为6,优先选取右方节点
此时open列表中F值最小为6,优先选取右方节点
此时open列表中F值最小为6,优先选取右方节点
此时咱们发现红色节点已经被添加到open列表中,算法结束。从红色节点开始逆推,其父节点为7号,7号父节点为6号,6号父节点为5号…,最终获得检索路径为:绿色-1-2-5-6-7-红色
在上面的例子中,全部遇到已经在open列表中的节点从新计算F值都不会更小,没法作更新操做。
因此再举一个例子来演示这种状况。相同的搜索区域,假设竖向或横向移动须要消耗1,此次也支持斜方向移动了,可是斜方向可能都是些山路很差走,移动一次须要消耗4。对应的相邻节点F值以下图所示
一样选择open列表中F值最小的节点,咱们优先选择了右方节点,计算其相邻节点。共8个。其中三个是黑色节点,一个绿色节点在close列表中,不考虑。上方两个和下方两个都是已经在open列表中了,要从新计算F值。
先看左上角的相邻节点,经过黄色节点到达改节点,H(n) = 5,G(n)不变,F(n)反而更大了,因此什么也不作。左下角节点同理。
上方居中节点,经过黄色节点计算H(n) = 2, G(n)不变,F(n) = 6 < 8 因此,更新这个节点的F值,并将其父节点修改成黄色节点。下方居中节点同理。
写了一套A*算法的Lua实现。主要特色以下:
优化效率,采用了map缓存,避免屡次循环遍历
支持配置移动权重
支持配置是否能够斜向移动,斜向时墙角是否可通行
源码请查看:https://github.com/iwiniwin/LuaKit/blob/master/AStar.lua