我想从根本上理解A *寻路的工作方式。任何代码或伪代码实现以及可视化都将有所帮助。
我想从根本上理解A *寻路的工作方式。任何代码或伪代码实现以及可视化都将有所帮助。
Answers:
可在网上找到大量的A *代码示例和说明。这个问题也收到了很多很好的答案,并提供了许多有用的链接。在我的回答中,我将尝试提供该算法的一个示例,该示例可能比代码或描述更容易理解。
为了理解A *,建议您首先看看Dijkstra的算法。让我指导您完成Dijkstra算法将执行搜索的步骤。
我们的起始节点是A
,我们想找到的最短路径F
。图的每个边缘都有与之相关的移动成本(在边缘旁边以黑色数字表示)。我们的目标是评估图形的每个顶点(或节点)的最小行驶成本,直到我们达到目标节点为止。
这是我们的出发点。我们有一个列表节点要检查,该列表当前为:
{ A(0) }
A
的成本为0
,所有其他节点都设置为无穷大(在典型的实现中,这将是类似int.MAX_VALUE
或相似的)。
我们从节点列表中选择成本最低的节点(因为列表仅包含A
,这是我们的候选者)并访问其所有邻居。我们将每个邻居的成本设置为:
Cost_of_Edge + Cost_of_previous_Node
并跟踪上一个节点(在该节点下方显示为粉红色的小字母)。A
可以立即标记为已解决(红色),这样我们就不再访问它。现在,我们的候选人列表如下所示:
{ B(2), D(3), C(4) }
同样,我们从列表(B
)中获取成本最低的节点,并评估其邻居。到的路径D
比的当前成本昂贵D
,因此可以丢弃此路径。E
将被添加到我们的候选人列表中,现在看起来像这样:
{ D(3), C(4), E(4) }
现在要检查的下一个节点D
。由于C
路径不短于现有成本,因此可以放弃与的连接。我们确实找到了一条更短的路径E
,因此E
将更新其之前节点的成本。现在,我们的列表如下所示:
{ E(3), C(4) }
因此,就像我们之前所做的那样,我们从清单(现在为)中检查成本最低的节点E
。E
只有一个未解决的邻居,这也是目标节点。到达目标节点的成本设置为10
,将其先前节点的成本设置为E
。现在,我们的候选人列表如下所示:
{ C(4), F(10) }
接下来我们检查C
。我们可以更新的费用和上一个节点F
。由于我们的列表现在具有F
成本最低的节点,因此我们完成了。我们的路径可以通过回溯先前最短的节点来构建。
因此,您可能想知道为什么我向您解释了Dijkstra而不是A *算法?好吧,唯一的区别在于您如何衡量(或排序)候选人。使用Dijkstra,它是:
Cost_of_Edge + Cost_of_previous_Node
使用A *,它是:
Cost_of_Edge + Cost_of_previous_Node + Estimated_Cost_to_reach_Target_from(Node)
其中Estimated_Cost_to_reach_Target_from
通常称为启发式函数。此功能将尝试估算到达目标节点的成本。良好的启发式功能将实现,只需访问较少的节点即可找到目标。尽管Dijkstra的算法将扩展到所有方面,但A *将(由于启发式)在目标方向上进行搜索。
阿米特(Amit)关于启发式的页面对常见的启发式有很好的概述。
A *路径查找是使用其他启发式的最佳优先类型搜索。
您需要做的第一件事是划分您的搜索区域。对于此说明,地图是一个正方形的方格网格,因为大多数2D游戏都使用一个方格网格,并且因为这很容易可视化。但是请注意,搜索区域可以按照您想要的任何方式进行分解:可能是十六进制网格,甚至是诸如Risk之类的任意形状。各种地图位置称为“节点”,只要您有一堆要遍历的节点并在节点之间定义了连接,该算法便会起作用。
无论如何,从给定的起始图块开始:
根据以下条件对开始的图块周围的8个图块进行“评分”:a)从当前图块移动到下一个图块的成本(水平或垂直移动通常为1,对角线移动通常为sqrt(2))。
然后,为每个图块分配一个附加的“启发式”得分-移至每个图块的相对价值的近似值。使用了不同的启发式方法,最简单的方法是给定图块的中心与端部图块之间的直线距离。
然后,当前“图块”被“关闭”,代理将移动到打开,具有最低移动得分和最低启发式得分的相邻图块。
重复此过程,直到到达目标节点,或者不再有打开的节点(意味着代理已被阻止)。
有关图示这些步骤的图表,请参阅此入门指南。
可以进行一些改进,主要是在改进启发式方法方面:
考虑到地形差异,粗糙度,陡度等
在网格上进行“扫掠”以遮挡地图上不是有效路径的区域有时也很有用,例如面向代理的U形。如果没有扫描测试,特工将首先进入U,转身,然后离开并在U的边缘四处走动。“真正的”智能特工会注意到U形陷阱并只是避开了它。扫频可以帮助模拟这一点。
我向您指出资源可能比尝试解释整个算法要好。另外,当您阅读Wiki文章时,请进行演示,看看是否可以直观地看到它的工作方式。如果您有特定问题,请发表评论。
在处理A *和Dijkstra的算法时,可视化的一件事很重要,那就是A *是定向的。它试图通过“猜测”要寻找的方向来找到到达特定点的最短路径。Dijkstra的算法查找到/ every /点的最短路径。
因此,就像第一个陈述一样,A *本质上是一种图探索算法。通常在游戏中,我们使用图块或其他世界几何图形作为图形,但您可以将A *用于其他内容。图遍历的两个ur算法是深度优先搜索和宽度优先搜索。在DFS中,在查看当前节点的同级之前,您始终会完全浏览当前分支,而在BFS中,您始终会先查看同级,然后再查看子级。A *试图找到它们之间的中间地带,当您接近期望的目标时,您会探索一个分支(更像DFS),但有时会停下来尝试同级兄弟,如果它在其分支下可能会有更好的结果。实际的数学方法是,您保留可能的节点列表,以探索下一个每个节点都具有“好处”的节点 分数表示(在某种抽象意义上)离目标有多近,分数越低越好(0表示您已找到目标)。通过找到最低分数加上远离根的节点数(通常是当前配置或寻路中的当前位置),可以选择下一步使用哪个。每次浏览一个节点时,都会将其所有子节点添加到此列表中,然后选择新的最佳节点。
在抽象级别,A *的工作方式如下: