迪杰斯特拉算法c++_寻路算法之A*算法

学习A星算法的实现,要先将迪杰斯特拉算法研究清楚。

A*相对于迪杰斯特拉算法不同点在于,加了一个启发系数,极大降低了搜索空间,提高算法效率,没有这个算法估计很多游戏的NPC寻路都跑不动。

迪杰斯特拉算法在寻找下个点时,选择标准为:

open列表中,距离起点最近的点。即:起点到该点的距离最短

最佳优先搜索算法在寻找下个点时,选择标准为:

open列表中,距离终点最近的点。即:起点到该点的距离最短

A*算法在寻找下个点时,选择标准为:

open列表中,起点到该点的距离 + 该点到终点的直线距离 之和 最短。


Dijkstra算法寻路过程:

在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。

在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。

37b418eece7bc8eb1532f14bdf768468.gif

我们可以看到,它总是广度遍历去找目标点;

当无障碍物时,就像水波一样向四周荡漾(左图),当有了障碍物时,他能围绕障碍物荡漾(右图)。


最佳优先搜索算法寻路过程:靠近目标点优先

在一些情况下,如果我们可以预先计算出每个节点到终点的直线距离,则我们可以利用这个信息更快的到达终点。

其原理也很简单。与Dijkstra算法类似,我们也使用一个优先队列,但此时以每个节点到达终点的距离作为优先级,每次始终选取到终点移动代价最小(离终点最近)的节点作为下一个遍历的节点。这种算法称之为最佳优先(Best First)算法。

32aaf3c60ff1fbd6954ca2e63855e7b3.gif

上图是迪杰斯特拉算法和最佳优先算法,在无障碍物下的对比。

我们可以看到,在没有障碍的情况下,最佳优先的速度很快,而迪杰斯特拉算法还 是波浪形的去搜索。

当有障碍物的情况下,迪杰斯特拉算法和最佳优先算法的寻路过程对比:

14594678e117774869f77e89241f6c74.gif

我们可以看到,当遇到障碍时,最佳优先算法 大体趋势会沿着起点到终点的直线为对称轴,左右来回寻点。

从上图可以看出,和 迪杰斯特拉算法相比,最佳优先算法得到的最终路径不一定是最短路径

有点向大门内的狗狗一样,望着门外的陌生人来回左右踱步狂吠。


迪杰斯特拉算法 和 最佳优先算法 的 联系

迪杰斯特拉算法:计算寻路的过程是圆圈向外扩散,比较耗,但是最终路径是最短的路径;

最佳优先算法:计算过程是目标导向型的,过程比较快,但是遇到障碍物时,最终路径不一定是最短的路径;

A*搜索算法

有了上述的两种算法之后,开始正式引入A*算法。

A*就好像结合了上述两种算法的结合体一样,它是根据:起点到该点的距离 + 该点到目标点的距离 之和 来寻路的。

即:

f(n)=g(n)+h(n)

其中:

  • f(n) 是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  • g(n) 是节点n距离起点的代价。
  • h(n) 是节点n距离终点的预计代价,这也就是A*算法的启发函数。

A*算法在运算过程中,每次从优先队列中选取$f(n)$值最小(优先级最高)的节点作为下一个待遍历的节点。

另外,A*算法使用两个集合来表示待遍历的节点,与已经遍历过的节点,这通常称之为open_set和close_set。

完整的A*算法描述如下:

* 初始化open_set和close_set;
* 将起点加入open_set中,并设置优先级为0(优先级最高);
* 如果open_set不为空,则从open_set中选取优先级最高的节点n:
    * 如果节点n为终点,则:
        * 从终点开始逐步追踪parent节点,一直达到起点;
        * 返回找到的结果路径,算法结束;
    * 如果节点n不是终点,则:
        * 将节点n从open_set中删除,并加入close_set中;
        * 遍历节点n所有的邻近节点:
            * 如果邻近节点m在close_set中,则:
                * 跳过,选取下一个邻近节点
            * 如果邻近节点m在open_set中,则:
                * 判断节点n到节点m的 F(n) + cost[n,m] 值是否 < 节点m的 F(m) 。来尝试更新该点,重新设置f值和父节点等数据
            * 如果邻近节点m也不在open_set中,则:
                * 设置节点m的parent为节点n
                * 计算节点m的优先级
                * 将节点m加入open_set中

A星算法 公式优化:

我们都知道,A星算法的公式为:f(n)=g(n)+h(n)

这是迪杰斯特拉算法和最佳优先算法的结合体。我们可以加个权重K来调节这两种算法的影响程度

f(n)= k * g(n) + (1-k) * h(n) ; k 取值 为 [0,1]

  • 当k=0时,f(n)= h(n) ,A星算法就变成了最佳优先算法;以距离目标最近为导向
  • 当k=1时,f(n)= g(n) ,A星算法就变成了迪杰斯特拉算法;以距离自己最近为导向
  • 当k=0.5时,f(n)=g(n)+h(n) ,A星算法就变成了大家提的A星算法;以距离自己最近 + 距离目标最近 为导向

我们可以通过调节权重K的值,来调节这两种算法的影响程度

我最终发现:

  • 算法的权重K=0时,使得花费较少的步骤即可得到答案。即 最佳优先算法,以目标距离为导向。但是最终的路径并不是最短最优的。
  • 算法的权重K=1时,花费的步骤是最多的方可得到答案。即 迪杰斯特拉算法,以圆圈波浪为导向。但是最终的路径是最短最优的。
  • 算法的权重K=0.5时,最佳优先算法的步骤 < 花费的步骤 < 迪杰斯特拉算法的步骤; 最佳优先算法的最终的路径 > 最终的路径 > 迪杰斯特拉算法的最终的路径。

结论,无障碍物时,最佳优先算法的步骤和结果路径都是最优的。有障碍物时,最佳优先算法的步骤依然最优,但是结果路径不是太理想。因此采用居中策略,采用A*算法,采用寻路步骤和结果路径都合适的方案。


关于距离

1.曼哈顿距离

如果图形中只允许朝上下左右四个方向移动,则启发函数可以使用曼哈顿距离,它的计算方法如下图所示:

685e2ecea22d0b07ccf109d8fcab9293.png
function heuristic(node) =     
    dx = abs(node.x - goal.x)     
    dy = abs(node.y - goal.y)     
   return D * (dx + dy) 

2.对角距离

如果图形中允许斜着朝邻近的节点移动,则启发函数可以使用对角距离。它的计算方法如下:

a9a2539e4a33cf4341e7387914f0775d.png

计算对角距离的函数如下,这里的D2指的是两个斜着相邻节点之间的移动代价。如果所有节点都正方形,则其值就是

e2b2aa736e37afa2b1d9726c57b9ae76.png
function heuristic(node) =     
    dx = abs(node.x - goal.x)     
    dy = abs(node.y - goal.y)     
   return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy) 

3.欧几里得距离

如果图形中允许朝任意方向移动,则可以使用欧几里得距离。

欧几里得距离是指两个节点之间的直线距离,因此其计算方法也是我们比较熟悉的:

f3abdae513600b09c2ee3f4bb908a57f.png

其函数表示如下:

function heuristic(node) =     
    dx = abs(node.x - goal.x)     
    dy = abs(node.y - goal.y)     
   return D * sqrt(dx * dx + dy * dy) 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值