A-star算法概述及其在游戏开发中的应用分析

回想起曾学习A*寻径算法时,难以透彻理解其原理和机制,但随着对图和搜索算法的理解愈发深入,近期重拾A-star时发现并没有那么困难。因此对A*算法和A*变种算法进行系统地学习,同时对其在游戏开发中的应用做了更深层次上的了解。

首先需要感谢Amit's A star PageA*算法中译本,让我能够全面地了解A*算法,下面大部分内容也是由原文及中译文中提炼而得。


1 从Dijkstra算法和最佳优先搜索到A*算法

1.1 Dijkstra算法

核心思想:

每次都选取距离起始点最近的点,并加入完成列表。算法的效率并不高,但对于没有负数权值边的情况能够得到从起始点到大部分点的最短路径。Dijkstra算法相当于启发式函数h(x) = 0的A*算法(在后面会详细说明)。

适用领域:

相对于A*算法,Dijkstra算法也有它适用的领域。当移动单位需要找到从其当前位置出发到N个分散目的地中的一个的最短路径(比如盟军只需抢占N个据点中的任意一个就能取胜,此时盟军需要选择一个离自己最近的据点i即可),Dijkstra算法会比A*算法更为合适。

原因在于,Dijkstra对所有中间节点一视同仁,并不考虑它们到目的地的实际代价,这就导致该算法会偏执地选择离起点最近的节点,而当当前扩展节点集中出现了任意一个目的地i,算法就可以终止,而得到的目的地i就是N个目的地中距起始点最近的目的地。相反地,A*算法则需要找到从起始点出发到所有目的地的最短路径,通过比较得到所有最短路径中的最小值。而实际上我们并不需要知道除了最近的目的地之外的其他目的地的路径就是如何。

总而言之,A*算法更适用于单点对单点的寻径;Dijkstra算法更适用于单点到多点的寻径。


1.2 最佳优先搜索算法(Best-fit)

核心思想:

最佳优先搜索算法的思想恰恰与Dijkstra算法相反,它忽略了从起始点到当前节点所花费的实际代价,而偏执地选择当前节点集中“它认为”距目的地最近的节点并进行拓展。之所以称为“它认为”,那是因为当前节点并不知道它到目的地的距离,而是通过一个评价函数(或称启发式函数)来估计从当前位置到达目的地的距离。由此可知,评价函数的选择对结果和效率会有较大的影响。

适用领域:

不可否认,最佳优先搜索算法拓展的节点具有明显的方向性(没有障碍时方向会始终指向目的地),从而忽略了很多远离目的地的节点,只有当迫不得已时,才会去拓展那些节点。这样优秀的品质使得Best-fit算法的效率比Dijkstra算法要高很多。但速度快也是要付出相应代价的,很可惜Best-fit算法很多时候并不能找到最短路径。同时,最佳优先搜索算法一般也只适用于单点对单点的寻径。


1.3 A*算法

核心思想:

A*算法很巧妙地结合了Dijkstra算法和Best-fit算法的优点,一方面通过设定代价函数g(x)来考虑从起始点到当前点的实际代价,另一方面通过设定启发式函数h(x)来考虑从当前点到目的地的估计代价,f(x) = g(x) + h(x)。它是从当前点击中取出f值最小的节点并进行拓展。因此A*算法具备了Best-fit算法的效率,同时又兼顾Dijkstra算法的最短寻径能力。

适用范围:

适用于单点对单点的寻径。而且可以得到最短路径,效率较高。


2 A*算法的核心思想

2.1 代价函数和启发式函数的权衡

代价函数g:g代表从起始点到当前节点的实际代价,每次拓展时将当前节点的g值加上从当前节点走向拓展节点所花费的实际代价,就是拓展节点的实际代价(从起始点到拓展节点)。

启发式函数h:h代表从当前节点到目的地的估计代价,可以采用曼哈顿距离、对角线距离、欧几里得距离来估计,但一般不建议使用欧几里得距离(因为欧几里得距离比另外两种距离要小,虽然仍可以得到最短路径,但计算的时间开销更大)。

g和h的权衡:代价函数g和启发式函数h的相对关系会影响到效率。当g比h占据更多权重时,A*算法更贴近于Dijkstra算法,算法效率降低。当g比h占据更少权重时,A*算法更贴近于Best-fit算法,算法效率提高,但可能在寻找最优解的路上走得更加曲折。此外应注意,只有当启发式函数h估计的代价小于等于真实代价时,才一定能得到最短路径;否则,必须在代码中做一些修改才可以得到最短路径(后面详细说明)。


2.2 代价函数和启发式函数的修正

上一节已经说明g和h的相对关系会影响效率和结果。因此也发展出了一系列调整代价函数和启发式函数的方法。权衡和修正代价函数和启发式函数是一个很tricky的过程,当我们增加g的权重,那么我们会得到最短路径但计算速度变慢;如果我们增加h则可能放弃了最短路径,同时使得A*算法更快。

但反观我们使用A*算法的初衷,我们只是希望得到一个较好的路径使得游戏玩家到达目的地,而且游戏玩家通过主观也无法精确判定他所走的路径是否是最佳路径。因此我们可以在修正中略微增加启发式函数h的比重,从而使得A*算法更快。

但应注意,g和h的衡量单位必须一致,其中任意一个值过分的大都有可能造成A*退化为Best-fit或Dijkstra算法。

a) g'(n) = 1 + α * (g(n) - 1):

代价函数g可以是1到实际g(n)间的一个数,这个数取决于你对效率的要求。

当α=0时,g'(n)=1,相当于

但不能过分的小,否则会退化为Best-fit算法。

b) f(n) = g(n) + w(n) * h(n):

w(n)≥1且随着当前点接近目的地而减小。设置这个系数的目的:在游戏寻径过程中,响应速度较为重要。因此在寻径初期增加w(n)值能够使其寻径速度增加,而且此时的寻径准确度实际上并不重要。而当接近目的地时,减小w(n)使得寻径更为准确。

c) h(n) = h(n) * (1 + l/p):

其中,p为所能接受的最大路径长度,l为移动一步的最小代价。

d) 向量叉积修正法:

①我们首先构造两个向量:第一个向量是从起始点->目的地的向量vector1(dx1, dy1),第二个向量是从当前点->目的地的向量vector2(dx2, dy2)。

②现在我们对vector1和vector2求叉积:crossProduct = vector1 x vector2 = |dx1*dy2 - dx2*dy1|

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值