【A星算法的优化方案】

当地图很大的时候,或者使用A星算法的寻路频率很高的时候,普通的A星算法就会消耗大量的CPU性能急剧下降,普通的A星性能还是不过关。接下来我们讲讲A星寻路在遇到性能瓶颈时的优化方案。

一、长距离导航

当距离很大,中间有很多障碍物时,A星的算法就会遇到瓶颈,不断加入的可行走点使得排序速度越来越慢,最后可能造成CPU阻塞无法动弹。

当寻路距离太大时怎么办?

其实路径不是非得实时计算出来才好,我们可以把一些常用的路径,在离线下算好放在数据文件中,游戏开启时放在内存里,当需要寻路到那个节点或者那个节点附近时,就可以取出来直接使用,而不再需要计算。

比如 A到B 我们已经算法路径并存放在了内存里,当我们在A附近,要寻路到B附近时,就可以先寻路到A,再调出A到B的路径,再计算B到目的的路径。

以这种方式来规避一些计算量特别大,或者计算频率特别高的寻路算法调用,在大型世界的RPG游戏里特别常用。

另一种方法为制作导航点的方式。什么是导航点呢?就是去目的地的必经的点位。

比如在一座城市里,有4个出口点,这个4个出口点就可以称为导航点,去目的地的路上肯定会经过这4个点的其中一个,我们在寻路时可以先寻找到最近的一个出口的导航点,当到达导航点后,再次寻找到目的地去的路径,有可能在这条路径上,还有一些导航点,继续寻找最近的导航点,依次类推,直到没有导航点可寻时,则直接寻路到目的地。

二、A星的排序算法优化

每次插入open列表的点后,open就不再是有序的队列了,所以每次去拿最小值时都需要重新排序。排序的时间消耗随着队列长度的增大而增大,其实有很大一部分A星的性能消耗都在排序上。

那么是不是可以不排序,其实可以不排序,而使用找到插入位置并插入的方法,让队列永远保持有序状态。

因为open队列在插入前是有序的,所以我们可以选用二分查找算法来找到插入的位置。

每次插入时都使用二分查找算法查找插入点,那么每次的插入复杂度为O(logN),比快排一次的O(NlogN)要快很多。

三、优化排序判断依据的预测值计算方法

前面用图所举的例子中,都是以当前点p与终点e之间的距离来作为预测值,这种方法简单但也不科学,导致A星寻找更好的点位时总是要绕很大的弯路。

我们可以改变一下这个策略,选用一个更科学的方法, F 预测值= G 当前最近步数 + H 预测当前点到终点的步数 的方法。

F 为预测值,G为起点s到当前点p的最近步数,H为当前点p到终点e的预测值,它们相加为F预测值。

其中G应该是已经计算好并放入节点中的值,该值就是计算过程中起点s到当前点的步数。我们可以把每步计算好的步数都放入节点中,以待需要计算时使用。

这个方法相当于,原来只关注当前点到终点的距离,变为关注起点s经过当前点p路径到终点e的距离,虽然还是贪婪的简单预测算法,但比起原来只关注当前点p与终点e的距离,更加科学化,能更快的找到更好更近的点位。

为什么要改善这个预测值,改善预测值有什么意义呢?

其实预测值的计算方法代表了在寻路过程中的走法,如果预测值只关注在于终点距离最近的点上,那么在寻路过程中的选择点位的顺序就会偏向于与终点更近的点。而如果预测值计算公式,关注的是整个距离较近的点位上,那么在寻路过程中在选择点位上也就会偏向整条路径短的方向上去靠。

四、多次频繁A星寻路的优化

多次频繁寻路中,对A星算法中每个运算,每行代码的运算细节都会有比较重大的考验。

比如我们在查看一个节点是否为被取过的节点,即是否为Close,很多人都会在Close里取寻找该节点是否存在,这个操作明显就没有考虑到性能的消耗,要在Close列表中找节点,就相当于遍历一遍所有已经找过的节点,Close里的节点越多,越浪费CPU,而且是不只一次浪费,每个循环都会浪费一次,性能消耗巨大。

因此我们通常的做法是把节点作为一个实例,在实例中添加IsClose的变量,来判断是否被取过,或者说是否Close。

但这种方法还是不够,因为IsClose变量是要初始化的,每次寻路都要将前面寻路过的痕迹抹去才能开始全新的寻路过程。

这就是又一个被很多人忽视的初始化的性能消耗,每次在A星寻路开始前,需要将IsClose的变量初始化为false,就需要遍历整个数据来初始化。

每次都要遍历整个数据的话,A星算法无论优化的多快都无济于事了,因为初始化的性能消耗就已经将A星的性能消耗完全盖掉了。如果初始化的性能消耗需要遍历整个数据,那么优化A星算法的意义何在。

其实可以用一个变量就能判断IsClose的方法,无需初始化。

我们可以在寻路类中设置一个属性变量FindIndex,或者专门为寻路服务的静态变量也可以,而每个寻路节点中也存有一个变量FindIndex,每次寻路前都对FindIndex++,在判断IsClose时,当节点中的FindIndex与寻路类中FindIndex一致时说明已经被当次寻路算法取出过,否则两者不一样,说明这个节点没有被取出过,当节点被取出时,节点里的FindIndex则设置为当前寻路类中的FindIndex值,以表明该节点已经被这次寻路算法取出过。

用一个变量和整数的比较就能知道IsClose的结果,省去了巨量的初始化操作。

在A星算法这种经常用频繁用的算法中,一个小小的性能消耗就能放大很多倍,特别注意调用的函数的复杂度,公式的复杂度,以及运算的优化,尽量做到能不调用函数的不调用函数,能简化公式的尽量简化公式,能用&|<>位运算符号代替加减乘除的尽量用位运算代替,节省A星算法的性能开销。

我也看过所谓的B星算法过程,其实世上没有B星,所谓的B星其实就是我们A星的优化版本,而且互联网中所阐述的B星算法存在一定几率无法寻到路径的问题,即它只关注所谓的‘前方‘,而忽略了其实’后方‘也有路,如果只有后方有路时,B星就无法找到路径。

转载自:https://www.jianshu.com/p/0e696bf0d0b8

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值