启发式

启发式函数h(n)告诉A * 估计从任何顶点n到目标的最小成本。选择一个好的启发式函数是很重要的事情。

用启发式算法的A*

启发式可用于控制A *的行为。

 

  • 在一个极端,如果 h(n) 是0,那么只 g(n) 起作用,A* 变成 Dijkstra算法,保证找到最短路径。
  • 如果 h(n)总是低于(或等于)从n目标移动到目标的成本,则 A* 保证找到最短路径。h(n) 越低,节点 A* 扩展越多,使其变慢。
  • 如果 h(n)完全等于从 n目标移动到目标的成本,那么 A* 将只遵循最佳路径并且永远不会扩展其他任何东西,使其非常快。虽然你不能在所有情况下都能实现这一点,但你可以在某些特殊情况下做到这一点。很高兴知道,鉴于完美的信息,A *将表现得非常完美。
  • 如果 h(n)有时大于从 n目标移动到目标的成本,那么 A* 不能保证找到最短路径,但它可以更快地运行。
  • 在另一个极端,如果 h(n)相对于 g(n) 非常高,那么 h(n) 只起一个作用,A *变成贪婪的最佳先搜索。

 

注意:
从技术上讲,如果启发式算法低估了实际成本,则应简单地将 A* 算法称为 A。但是,我将继续将其称为 A*,因为实现是相同的,并且游戏编程社区不区分 和 A*

 

所以我们有这样一个有趣的情况,我们可以决定我们想要从 A* 中获得什么。

在恰当的位置,我们会很快得到最短的路径。

如果我们设定地太低,那么我们将继续获得最短的路径,但它会慢下来。

如果我们设定地太高,那么我们放弃最短路径,但 A* 会跑得更快。

 

在游戏中,A* 的这个属性非常有用。

例如,您可能会发现在某些情况下,您宁愿拥有一条“好”路径而不是“完美”路径。

要在 g(n)和 h(n)之间寻找平衡性,您可以修改其中任何一个。

 

速度还是准确度?

A* 基于启发式和成本函数改变其行为的能力在游戏中非常有用。

可以利用速度和准确度之间的权衡来加快游戏速度。

对于大多数的游戏,你不真正需要的最好的两个点之间的路径。

你需要一些接近的东西。你需要什么可能取决于游戏中发生了什么,或者计算机的速度有多快。

 

假设你的游戏有两种类型的地形,Flat 和 Mountain,平地的移动成本是1,山的移动成本是3。

A* 沿着平坦的土地搜索 3 次,和山地的 1 次是相同的开销。这是因为它可能在平坦的地形上有一条环绕山脉的路径。

您可以使用 1.5 作为两个平地之间的启发式距离来加速 A* 的搜索。这样的话,A* 将比较 3 和 1.5,它看起来不会像比较 3 和 1 那样糟糕。

这样的话,山地看起来并非那样令人不满意(开销过大),所以它不会花费太多时间试图找到其他的解决方法。

或者,您可以通过减少搜索山脉周围路径的数量来加快 A* 的搜索速度 - 即告诉 A* 山上的移动费用是 2 而不是 3。

这样的话,沿平坦地形搜索 2 次,和沿着山区地形 1 次的开销相同。

这两种方法都放弃了最理想的路径来更快地获得结果。

 

速度和准确度之间的选择不必是静态的。

您可以根据 CPU 速度,进入寻路的时间比例,地图上的单位数量,单位的重要性,组的大小,难度级别或任何其他因素动态选择。

使权衡动态的一种方法是构建一个启发式函数,假设行进一个网格空间的最小成本为1,然后构建一个可扩展的成本函数:

如果 alpha 为 0,则修改的成本函数将始终为 1。

这样设置的话,会完全忽略地形成本,并且 A* 在网格中会简单地进行 可通过/不可通过 的判断。

如果 alpha 为 1,则将使用原始成本函数,您将获得 A* 的全部的优点。

您可以将 alpha 在两者之间进行调整。

 

您也应该考虑在启发式返回的绝对最低成本预期的最低成本之间进行权衡。

例如,如果您的大部分地图都是草地,移动成本为 2,但地图上的某些地方的道路的移动成本为 1,那么您可能会考虑到启发式假设没有道路的情况,然后返回 2 * distance

 

速度和准确度之间的选择不一定是全局的。

在地图某些区域的准确性可能很重要,我们可以根据这些来动态选择一些内容。

 

例如,我们在靠近某些位置时,需要一条最佳路径。

基于这个假设,在这个位置附近时,我们可能需要重新计算路径。

而远离这个位置时,为什么要对路径的准确性做要求呢?

在地图的安全区域拥有最短的路径也许并不是那么重要,但是当偷偷溜过一个敌人的村庄时,安全和快速是必不可少的。

 

衡量单位

A* 计算 f(n) = g(n) + h(n)

要添加两个值,这两个值必须处于相同的衡量单位。

如果 g(n) 以小时为单位而 h(n) 以米为单位进行测量,则 A* 将考虑 g或 过多或过少,并且您要么不会获得良好的路径,要么 A* 将会运行得比预期要慢。

 

具体的启发式

如果您的启发式方法与最佳路径上的距离完全相等,您将看到 A* 扩展的节点非常少,如下一节的图表所示。

A* 算法内部发生的事情是它在每个节点上计算 f(n) = g(n) + h(n)。

当 h(n)完全匹配 g(n)时f(n)的值不沿路径更改。

不在正确路径上的所有节点,将会比在正确路径上的节点,具有值更高的 f。

由于 A* 在考虑较低值 f 节点之前不考虑 f 值较高的节点,因此它永远不会偏离最短路径。

预先计算

构造具体的启发式的一种方法是预先计算每两个点之间的最短路径的长度。

这对于大多数游戏地图来说是不可行的。但是,有一些方法来近似这种启发式:

 

  • 使用包含数个小网格的大网格。预先计算任何一对大网格位置之间的最短路径。
  • 预先计算任何一对路径点之间的最短路径。这是使用大网格方法的一种形式。

 

然后添加一个启发式算法 h',估算从任何位置到附近航路点的费用(如果需要,这个也可以预先计算)。

最终的启发式将是:

 

或者,你也可以找一个更好的,但开销更大的启发式函数,上述 w1 和 w2 分别是接近起点和目标点的路径点。

 

网格地图的启发式算法

在网格上,有一些众所周知的启发式函数可供使用。

使用与允许的移动匹配的距离启发式:

  • 在允许4个运动方向的方格上,使用曼哈顿距离 (L1)。
  • 在允许8个运动方向的正方形网格上,使用对角线距离 (L∞)。
  • 在允许任何移动方向的方格上,您可能想要也可能不想要欧几里德距离 (L2)。如果A *在网格上找到路径但您不允许在网格上移动,则可能需要考虑地图的其他表示
  • 在允许6个方向移动的六边形网格上,使用适合六边形网格的曼哈顿距离。

 

将 [步长] 乘以 [步长的最小成本] 。

例如,如果您以米为单位测量,距离为3格,每个方格为15米,那么启发式将返回3⨉15= 45米。

如果你在时间上测量,距离是 3 个方格,每个方格需要至少 4 分钟才能穿过,然后启发式将返回 3 × 4 = 12 分钟。

启发式返回的单位(米,分钟等)应与成本函数使用的单位相匹配。

曼哈顿距离

方形网格的标准启发式是曼哈顿距离

查看您的成本函数,令一个格子移动到相邻格子的最低成本为 D。

在简单的情况下,您可以设置 D 为 1。

对方形网格来说,您可以在4个方向上移动的启发式方法,应该是 D ⨉ 曼哈顿距离:

需要怎么设置 D ?使用与您的成本函数匹配的衡量单位。

对于最佳路径和“允许的”启发式,将 D 设置为相邻方块之间的最低成本。

在没有障碍物,以及在移动成本 D 最小的地形上,向目标移动一步,g 增加 D,h 减少 D。

当这两个进行变化时,f ( 根据 g 和 h 变化) 将保持不变;这是启发式和成本函数衡量单位一致的表现。

您还可以放弃最佳路径,通过增加 D 或 降低 [最低和最高的边之间的开销的差值] 来使 A* 运行得更快。

(注意:上面的图片在启发式中解决了相等开销的情况。)

对角线距离

如果地图允许对角线移动,则需要使用不同的启发式方法。

(4 east, 4 north) 方向的曼哈顿距离将为 8 × D 。

但是,您可以简单地往 (4 northeast) 移动,因此启发式应该是 4 × D2,其中 D2 是对角移动的成本。

在这里,我们先计算出如果不能采取对角线时,一共会走的步数,然后减去你使用对角线的步数。

这里有 min(dx, dy) 步会走对角线,每一步有 D2 的开销,但可以节省原本的开销,非对角线时对应的开销为 2 × D。

 

当 D = 1 且 D2 = 1 时,这称为切比雪夫距离Chebyshev distance

当 D = 1 且 D2 = sqrt(2)时,这称为八分距离(octile distance

 

这种的另一个写法是 D * max(dx, dy) + (D2 - D) * min(dx, dy),这些都是等价的。

欧氏距离(Euclidean distance)

如果您的单位可以朝任何角度(而不是网格方向)移动,那么您应该使用直线距离:

 

但是,如果是这种情况,那么您可能无法直接使用 A*,因为成本函数 g 与启发式函数 h 不匹配

由于欧几里德距离比曼哈顿或对角线距离短,您仍然会获得最短路径,但 A* 将需要更长时间才能运行:

欧氏距离,平方

我看到几个介绍 A* 的网页,建议您使用距离的平方,从而避免欧几里德距离中开销昂贵的平方根:

不!要!这!样!做!

这肯定会遇到单位上的问题。

g 和 h 的单位要匹配,因为你将它们加在一起形成 f 。

当 A* 计算 f(n) = g(n) + h(n) 时,距离的平方将远远高于成本 g,你最终会得到一个高估的启发式。

对于更长的距离,这将接近 g(n),而无法对计算 f(n)产生帮助,并且 A* 将降级为贪心的最佳优先搜索:

 

要尝试解决此问题,您可以缩小启发式的影响。

然而,你会遇到了相反的问题:对于较短的距离,启发的影响和 g(n) 相比,将会太小,A* 会降级为 Dijkstra算法。

 

在分析后,如果您发现计算平方根的开销很高,可以尝试,换一种快速的方法来计算近似的平方根,或者使用对角线距离作为近似值。

 

多目标

如果你要搜索个目标,可以构建一个启发 h'(x)。

对目标点 h1, h2, h3, ... 来说,h1(x), h2(x), h3(x), ... 是各自到周围点的启发式。

而 h'(x) 是取 h1(x), h2(x), h3(x), ...中最小的那个。

 

可以这么来想,我们构造一个新的节点,对于每一个目标终点,我们添加一条开销为 0 的路径,连接它和新节点。

想要到达新节点,就必然要经过,某个终点连接新节点的路径。

 

如果你想搜索所有目标的路径,你最好的选择可能是 Dijkstra算法,当你找到所有目标时提前退出。

可能存在可以计算这些路径的 A* 的变体;我不知道。

 

如果您想搜索单个目标附近的地点,请询问 A* 搜索以找到目标区域中心的路径。从 OPEN 集处理节点时,在找到足够接近的节点时退出。

 

解决平局

在一些网格图中,有许多具有相同长度的路径。

例如,在没有地形变化的平坦区域中,使用网格将得到许多等长路径。A* 可能会探索具有相同 f 值的所有路径,而不是其中之一。

 

f 值打平时

快速解决此问题的方法是调整 g 或 h 值。打破平局需要使 f 值不同,且相对于顶点是确定性的(即,它不应该是随机数)。

由于 A* 搜索按 f 值排序,使 f 值不同意味着只会有其中一个等值的 f 会被搜索下去。

 

一种方法是微调 h。

如果我们缩小 h,那么随着我们向目标迈进,f 将会增加。不幸的是,这意味着 A* 将更喜欢将顶点扩展到靠近起点而不是靠近目标的顶点。

我们可以将 h 稍微调大(甚至0.1%)。A* 将倾向于扩展靠近目标的顶点。

p 应该小于(采取一步的最小成本)/(预期的最大路径长度)

假设您不希望路径长度超过1000步,则可以选择 p = 1/1000。(请注意,这略微打破了启发式的“可接受性”,但在游戏中它几乎从不重要。)

这种打破平局的结果是 A* 探索的范围将远远少于之前:

将打破平局的条件扩展进启发式

当有障碍的时候,它仍然需要探索找到绕过它们的方法,但请注意,在障碍物通过后,A* 探索的很少:

可以更直接地将 h 传递给比较函数。当 f 值相等时,比较函数会通过查看来打破平局 h

 

打破关系的另一种方法是在 启发式 或 边的开销 中添加确定性随机数。(选择确定性随机数的一种方法是计算坐标的散列。)

这比上面提到的、针对 h 的调整打破了更多的平局情况。

 

打破平局的另一种方法是优先选择从起点到目标的直线路径: 

这段代码计算 起点到终点向量 当前点到目标向量 的叉积。当这些向量不对齐时,叉积将更大。

结果是,这段代码会略微偏向沿着从开始到目标的直线路径的路径。

当没有障碍物时,A *不仅探索较少的地图,路径看起来也很好:

然而,因为这个破平条件是优先选择从起点到目标的直线路径,所以当绕过障碍物时会发生奇怪的事情(注意路径仍然是最佳的;但它看起来很奇怪):

要以交互方式探索这个平局破坏者的改进,请参阅James Macgill的A * applet

使用 “Clear” 清除地图,并在地图的对角上选择两个点。

当您使用 “Classic A*” 方法时,您将看到平局的效果。当您使用 “Fudge” 方法时,您将看到上述方法添加到启发式中的效果。

 

另一种打破平局的方法,是仔细构建您的 A* 优先级队列。

当插入新节点时,如果它的 f 值与之前插入的节点相同,新的将拥有更高(更低)的优先级。

 

还有一种在网格中打破平局关系的方法是尽量减少转弯。

节点到当前节点的 x,y 的变化告诉您当前移动的方向。

对于从 当前节点 到 邻居节点 的所有边缘,如果 x,y 的变化不同于从之前的变化,那么为运动成本增加一点点开销。

 

对启发式的上述修改是针对潜在的低效率情况的“创口贴”。

当有许多路径同样好时会发生这种情况,导致需要探索大量节点。我们偏向 “更聪明,而不是更努力” 的方法:

  • 替代地图呈现的方式可以通过减少图中的节点数来解决问题。将多个节点折叠为一个节点,或者删除除重要节点之外的所有节点。矩形对称缩减是一种在方格网格上执行此操作的方法;还要看“框架四叉树(framed quadtree)”。分层路径寻找使用具有少量节点的高级图来查找大部分路径,然后使用具有更多节点的低级图来细化路径。
  • 一些方法仅留下节点数量,但减少了访问的节点数量跳转点搜索会跳过包含大量关系的大面积节点,它专为方形网格设计。跳跃链接添加跳过边的“快捷方式”边缘。该 ALPHA* 算法增加了一些 深度优先搜索 到  A*的广度优先的行为,所以它可以得到一条路径,而不是同时处理所有的路径。
  • 边缘搜索(PDF)通过快速进行节点处理来解决问题。它不是一次保持OPEN集排序​​和访问节点,而是批量处理节点,仅扩展具有低 f 值的节点。这与HOT队列方法有关。

近似启发式

具有精确距离的启发式对于使 A* 变得更快速是理想的,但它通常是不切实际的。

我们通常可以预处理图形以构造近似距离,并在 A* 启发式中使用之。

 

ALT A* 使用“地标”和三角不等式来预处理寻路图,以便更快地进行寻路。

ALT 还做了一些其他的事情,但启发式改进是引起我注意的部分。它实现起来非常简单,有时只需 15 行代码,却可以产生令人印象深刻的加速。

 

“地标”这个名称有点误导。这些点需要放在地图的外边缘。一些作者称之为“差异启发式(differential heuristics)”。

 

地标的方法存储了大量可以压缩的数据。

压缩差分启发式(The Compressed Differential Heuristic∗)显示压缩地标数据的结果。

您可以在同一空间中存储更多地标,因此您可以获得改进的启发式值。

 

地标可能是更一般方法的特例。这篇论文探讨了将地图转换为常规的距离度量。

 

Distance oracles似乎是相关的,但我还没有研究它们。

 

posted on 2018-12-04 21:06 秋月疾风 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/xuuold/p/10059872.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值