启发式

启发式函数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算法。

原文地址:https://www.cnblogs.com/xuuold/p/10059872.html

时间: 2024-10-22 05:51:45

启发式的相关文章

【Splay】【启发式合并】hdu6133 Army Formations

题意:给你一颗树,每个结点的儿子数不超过2.每个结点有一个权值,一个结点的代价被定义为将其子树中所有结点的权值放入数组排序后,每个权值乘以其下标的和.让你计算所有结点的代价. 二叉树的条件没有用到. 每个结点开一个Splay,从叶子往上启发式合并上去,可以先bfs一遍确定合并顺序.每一次将Splay大小较小的结点的权值全提取出来塞到较大的里面. 由于权值可能重复出现,所以每个结点记个cnt. 答案统计的时候,就将那个刚塞进去的旋到根,然后答案加上左子树的权值和,再加上(右子树的权值的个数+该结点

BZOJ 4059 [Cerc2012]Non-boring sequences(启发式分治)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4059 [题目大意] 一个序列被称为是不无聊的,仅当它的每个连续子序列存在一个独一无二的数字, 即每个子序列里至少存在一个数字只出现一次. 给定一个整数序列,请你判断它是不是不无聊的. [题解] 预处理每个元素上一次出现位置和下一个出现位置, 我们发现对于一个子序列[L,R]来说, 如果存在pre[i]<L&&nxt[i]>R那么这个子序列一定是满足条件的, 否则就不

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

学习笔记::启发式合并

昨天做Tree Rotation,没发现自己写的是暴力,还要了数据...... 然后发现好像必须得用启发式合并 不想学线段树,学了个splay的 --------------------------------------------------- 假设现在有n个点,每个点是一个splay,互不连起来 假设我们每次让两个不连通的splay联通, 所谓启发式:就是把小的合并到大的上,这样使复杂度有保证 怎么合并呢?就是先把小的splay的每个点找出来,然后插入到大的splay ----------

启发式优化算法

Xin-She Yang (可能音译:杨新社)是一个国际知名学者,他在剑桥大学创新的启发式优化算法包括:萤火虫算法(2008年).布谷鸟搜索算法(2009)和蝙蝠算法(2010).他是英国国家物理实验室的高级研究科学家.

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l

[BZOJ 1483][HNOI 2009]梦幻补丁(有序表启发式合并)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1483 分析: 先将不同的颜色的出现位置从小到大用几条链表串起来,然后统计一下答案 对于每次修改,修改一下答案即可,修改之后需要将两个颜色的链表合并就行了,但感觉似乎会TLE? 以下摘录与Hzwer的blog: 1:将两个队列合并,有若干队列,总长度为n,直接合并,最坏O(N), 2:启发式合并呢? 每次我们把短的合并到长的上面去,O(短的长度) 咋看之下没有多大区别, 下面让我们看看

51nod 1907(多项式乘法启发式合并)

题目: 分析: 对于一个确定的生成子图,很明显是在一个连通块上走,走完了再跳到另一个连通块上,假设连通块个数为cnt,那么答案一定是$min(a_{cnt-1},a_cnt,..,a_{n-1})$  那现在的问题就是如何求出对于原图而言,连通块个数分别为1,2..n的生成子图的个数 我们去考虑每条边的贡献 在一个仙人掌上只有树边和回路上的边,对于树边如果删除那么肯定连通块个数+1,对于回路上的边,删除一条边不影响,再后面每删除一条边连通块个数+1 我们可以写出它们的生成函数,然后乘起来 对于树

BZOJ2733 永无乡【splay启发式合并】

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含

【BZOJ-4668】冷战 并查集 + 启发式合并 + 乱搞

4668: 冷战 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 37  Solved: 24[Submit][Status][Discuss] Description 1946 年 3 月 5 日,英国前首相温斯顿·丘吉尔在美国富尔顿发表“铁幕演说”,正式拉开了冷战序幕. 美国和苏联同为世界上的“超级大国”,为了争夺世界霸权,两国及其盟国展开了数十年的斗争.在这段时期,虽然分歧和冲突严重,但双方都尽力避免世界范围的大规模战争(第三次世界大战)爆发