启发式搜索在于当前搜索结点往下选择下一步结点时,可以通过一个启发函数来进行选择,选择代价最少的结点作为下一步搜索结点而跳转其上。
DFS和BFS在展开子结点时均属于盲目型搜索,它不会选择哪个结点在下一次搜索中更优而去跳转到该结点进行下一步的搜索。与DFS,BFS不同的是,选择的启发函数,可以很快得到一个搜索问题的最优解。
本文先介绍A*算法的原理,再进行了一种实现。
A*搜寻算法
A*搜寻算法,俗称A星算法,作为启发式搜索算法中的一种,这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上。该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。
A*算法最为核心的部分,就在于它的一个估值函数的设计上:
f(n)=g(n)+h(n)
其中f(n)是每个可能试探点的估值,它有两部分组成:
一部分,为g(n),它表示从起始搜索点到当前点的代价(通常用某结点在搜索树中的深度来表示)。
另一部分,即h(n),它表示启发式搜索中最为重要的一部分,即当前结点到目标结点的估值,
h(n)设计的好坏,直接影响着具有此种启发式函数的启发式算法的是否能称为A*算法。
一种具有f(n)=g(n)+h(n)策略的启发式算法能成为A*算法的充分条件是:
1、搜索树上存在着从起始点到终了点的最优路径。
2、问题域是有限的。
3、所有结点的子结点的搜索代价值>0。
4、h(n)=<h*(n) (h*(n)为实际问题的代价值)。
当此四个条件都满足时,一个具有f(n)=g(n)+h(n)策略的启发式算法能成为A*算法,并一定能找到最优解。
对于一个搜索问题,显然,条件1,2,3都是很容易满足的,而条件4: h(n)<=h*(n)是需要精心设计的,由于h*(n)显然是无法知道的,所以,一个满足条件4的启发策略h(n)就来的难能可贵了。
A*算法流程:
首先将起始结点S放入OPEN表,CLOSE表置空,算法开始时:
1、如果OPEN表不为空,从open表选取权值最小结点n,如果为空算法失败。
2、判断n是否为目标解?若是,则找到一个解(继续寻找或终止),否则到3。
3、将n的所有邻接结点展开,从n可以直接关联的结点(子结点),如果不在CLOSE表中,就将它们放入OPEN表,并把S放入CLOSE表,同时计算每一个后继结点的估价值f(n),回到1。
首先将起始结点S放入OPEN表,CLOSE表置空,算法开始时:
1、如果OPEN表不为空,从open表选取权值最小结点n,如果为空算法失败。
2、判断n是否为目标解?若是,则找到一个解(继续寻找或终止),否则到3。
3、将n的所有邻接结点展开,从n可以直接关联的结点(子结点),如果不在CLOSE表中,就将它们放入OPEN表,并把S放入CLOSE表,同时计算每一个后继结点的估价值f(n),回到1。
估值方法:
H值可以用很多不同的方法估算,最关键是选取最适用当时场景的。一个普通的方法是曼哈顿方法,它计算从当前格到目的格之间水平和垂直的方格的数量总和,忽略对角线方向。然后把结果乘以10。这被成为曼哈顿方法是因为它看起来像计算城市中从一个地方到另外一个地方的街区数,在那里你不能沿对角线方向穿过街区。很重要的一点,我们忽略了一切障碍物。这是对剩余距离的一个估算,而非实际值,这也是这一方法被称为启发式的原因。
A*算法找到的解不一定是真正意义上的最优解,它的寻路性能和找最优的性能受到估值函数的影响。
A*算法自动寻路的实现
场景:穿越一片丛林,有高有低,找出最佳行进路线。
实现:估值h函数使用曼哈顿距离或切比雪夫距离。采用JavaScript语言在前端上实现,利用HTML5中canvas画布在浏览器上显示地图和寻路过程。画布左上角设定为起点,画布右下角设定为终点。
Function函数对象:
Map 用于随机创建和存储地形数据。每个位置的value值代表了该位置的穿越难度,难度同时反映在颜色深度上。
PathFinder 用于绘制地图和自动寻路。
main 负责主函数流程。
随机地形生成的方法:
现实世界的崇山峻岭或丛林草原必定是连续变化的而不是离散的,所以构造地形的时候也要使地形情况是连续变化的。首先生成若干个随机值的点,然后从中选出值大于阈值max的点(认为是海拔高的地方),令其周围的点高度逐渐递减。
具体细节:点这里
参考资料: