启发函数 (Heuristic Function)

盲目搜索会浪费很多时间和空间, 所以我们在路径搜索时, 会首先选择最有希望的节点, 这种搜索称之为 "启发式搜索 (Heuristic Search)"

如何来界定"最有希望"? 我们需要通过 启发函数 (Heuristic Function) 计算得到.

对于网格地图来说, 如果只能四方向(上下左右)移动, 曼哈顿距离(Manhattan distance) 是最合适的启发函数.

function Manhattan(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)// 在最简单的情况下, D 可以取 1, 返回值即 dx + dyreturn D * (dx + dy)

如果网格地图可以八方向(包括斜对角)移动, 使用 切比雪夫距离(Chebyshev distance) 作为启发函数比较合适.

function Chebyshev(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)// max(dx, dy) 保证了斜对角的距离计算return D * max(dx, dy)

如果地图中允许任意方向移动, 不太建议使用网格 (Grid) 来描述地图, 可以考虑使用 路点 (Way Points) 或者 导航网格 (Navigation Meshes) , 此时使用 欧式距离(Euclidean distance) 来作为启发函数比较合适.

function heuristic(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)// 在最简单的情况下, D 可以取 1, 返回值即 sqrt(dx * dx + dy * dy)return D * sqrt(dx * dx + dy * dy)

欧式距离因为有一个 sqrt() 运算, 计算开销会增大, 所以可以使用 Octile 距离 来优化(不知道怎么翻译), Octile 的核心思想就是假定只能做 45 度角转弯.

function heuristic(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)k = sqrt(2) - 1return max(dx, dy) + k * min(dx, dy)

下图是一个启发函数的简单示意

Dijkstra

Dijkstra 算法 是由计算机大牛 Dijkstra 在1956年提出来的, 它是一个"精确"的寻路算法, 能保证发现节点之间的最短路径, 本质是一个 广度优先搜索 .

  • g(n): 从起始点到当前点 n 的开销, 在网格地图中一般就是步长.
  • 将起始点 S 加入 open-list.
  • 从当前的 open-list 中选取 g(n) 最优(最小)的节点 n, 加入 closed-list
  • 遍历 n 的后继节点 ns
  • 如果 ns 是新节点, 加入 open-list
  • 如果 ns 已经在 open-list 中, 并且当前 h(ns) 更优, 则更新 ns 并修改 parent
  • 迭代, 直到找打目标节点 D, 回溯得到路径

Dijkstra 算法的效率并不高, 时间复杂度为 O(n^2), 但是它保证了寻路结果是最短距离.

Best-First-Search

Best-first Search 最佳优先搜索, 简称为 BFS.

BFS 根据启发式函数的推断, 每次迭代最优的节点, 直到寻找到目标节点. 一个简单的算法流程如下:

  • h(n) 代表从当前点 n 到目标点 S 的估算开销, 即启发函数.
  • 将起始点 S 加入 open-list.
  • 从当前的 open-list 中选取 h(n) 最优(最小)的节点 n, 加入 closed-list
  • 遍历 n 的后继节点 ns
  • 如果 ns 是新节点, 加入 open-list
  • 如果 ns 已经在 open-list 中, 并且当前 h(ns) 更优, 则更新 ns 并修改 parent
  • 迭代, 直到找打目标节点 D, 回溯得到路径

如果不使用 open-list, 直接每次都寻找 n 的后继节点中最优的节点, BFS 则退化为贪心最佳优先搜索 (Greedy BFS).

不管是 BFS 还是 Greedy-BFS, 本质上都还是寻找 局部最优 , 虽然效率会比 Dijkstra 高很多, 但最终得到的解并不一定是全局最优(最短距离), 在地图中有障碍物时尤为明显.

A-Star

A-Star 俗称 A* , 是应用最广的寻路算法, 在很多场景下都适用. A* 兼顾了 Dijkstra 的准确度和 BFS 的效率.

  • f(n) = g(n) + h(n) , g(n) 和 h(n) 的定义同上
  • 当 g(n) 退化为 0, 只计算 h(n) 时, A* 即退化为 BFS.
  • 当 h(n) 退化为 0, 只计算 g(n) 时, A* 即退化为 Dijkstra 算法.
  • 将起始点 S 加入 open-list.
  • 从当前的 open-list 中选取 f(n) 最优(最小)的节点 n, 加入 close-list
  • 遍历 n 的后继节点 ns
  • 如果 ns 是新节点, 加入 open-list
  • 如果 ns 已经在 open-list 中, 并且当前 f(ns) 更优, 则更新 ns 并修改 parent
  • 迭代, 直到找打目标节点 D.

A* 算法在从起始点到目标点的过程中, 尝试平衡 g(n) 和 h(n), 目的是找到最小(最优)的 f(n) .

A-Star 算法衍生

如果在每一步迭代扩展中, 都去除掉质量比较差的节点, 即选取 有限数量的后继节点 , 这样减少了空间开销, 并提高了时间效率. 但是缺点可能会丢弃潜在的最佳方案. 这就是 集束搜索(Beam Search) , 本质上还是局部最优的贪心算法.

当距离起点越近, 快速前行更重要; 当距离目标点越近, 到达目标更重要. 基于这个原则, 可以引入权重因子, A* 可以演进为选取 f(n) = g(n) + w(n) * h(n) 最优的点, w(n) 表示在点 n 的权重. w(n) 随着当距离目标点越近而减小. 这是 动态加权 A* (Dynamic Weighting A*) .

当从出发点和目标点同时开始做双向搜索, 可以利用并行计算能力, 更快的获得计算结果, 这是 双向搜索 (Bidirectional Search) . 此时, 向前搜索 f(n) = g(start, n) + h(n, goal), 向后搜索 f(m) = g(m, goal) + h(start, m), 所以双向搜索最终可以归结为选取 g(start, n) + h(m, n) + g(m, goal) 最优的一对点(m, n).

A* 在静态地图中表现很棒, 但是在动态环境中(例如随机的障碍物), 却不太合适, 一个基于 A* 算法的 D* 算法(D-Star, Dynamic A*) 能很好的适应这样的动态环境. D* 算法中, 没有 g(n), f(n) 退化为 h(n), 但是每一步 h(n) 的计算有所不同: h(n) = h(n-1) + h(n-1, n) , c(n-1, n) 表示节点 n-1 到节点 n 的开销.

启发函数 (Heuristic Function) —Octile相关推荐

  1. A*算法中启发函数的使用

    A*算法使用启发函数h(n)来获得对于从任意结点n走到目标结点的最小代价的估计,因此选用一个好的启发函数是非常重要的. A*算法中启发函数的使用 启发函数可以用来控制A*算法的行为. 在极端情况下,如 ...

  2. 【 Verilog HDL 】函数(function)与任务(task)简单介绍

    背景 关于函数和任务,是我这几天一直想写的内容,原因在于我上篇博文:Verilog HDL 使用规范(一),最后提到的问题,关于代码书写的规范中,要求:用一个函数(function)来代替表达式的多次 ...

  3. c语言 宏 变长参数,科学网—C/C++中处理变长参数函数(Variadic Function)的几个宏 - 彭彬的博文...

    近日在模式中进行非线性方程组求解时遇到变长参数函数的问题,以前从来没有自己写过变长参数的函数,于是补了一下课,将近日对该小问题的学习和理解整理如下. 一.变长参数函数(variadic functio ...

  4. 【Android 逆向】IDA 工具使用 ( 函数窗口 Function window | 创建引用图 Xrefs graph to | 创建调用图 Xrefs graph from )

    文章目录 一.函数窗口 Function window 二.创建引用图 Xrefs graph to 三.创建调用图 Xrefs graph from 一.函数窗口 Function window 左 ...

  5. Python编程基础:第五十三节 匿名函数Lambda Function

    第五十三节 匿名函数Lambda Function 前言 实践 前言 匿名函数是一种非常优雅的表达方式,它可以将函数定义用一行代码进行表示.其书写方式为参数列表:函数实现,其中多个参数之间用逗号隔开, ...

  6. Python编程基础:第四十六节 super函数Super Function

    第四十六节 super函数Super Function 前言 实践 前言 使用super函数可以在子类中直接调用父类的方法.通常情况下,我们会将一些通用的属性或方法定义在父类中,子类可以直接使用父类中 ...

  7. 【学习笔记】17、函数(Function)的定义和调用

    函数(Function)的定义和调用 之前我们使用的print().input().max()等都是函数,只不过他们是内置函数,我们看不到它的实现方式,直接拿过来就可以使用了.而本讲所说的函数是根据我 ...

  8. jquery的2.0.3版本源码系列(2):21行-94行定义了一些变量和函数 jQuery=function(){}

    2.1.bug通过索引查询 这里的#13335是bug的索引,如何查询呢? 第一步,浏览器地址栏输入"https://bugs.jquery.com/". 第二步,在网页的搜索框里 ...

  9. ES6---箭头函数()={} 与function的区别(转载)

    1.箭头函数与function定义函数的写法: //function function fn(a, b){return a + b; } //arrow function var foo = (a, ...

  10. 为什么a*算法采用哈密尔顿距离作为启发函数比不在位数为启发函数的性能要好?_【论文研读】路径规划中的Hybrid A*算法...

    本文目的 由于在carla-autoware的示例中使用了hybrid A* 算法,所以本文基于以下两篇文章对hybrid A* 算法过程进行整理:(文中挑选了一些个人认为便于理解算法的图片,均来自于 ...

最新文章

  1. 在CentOS 6.9 64bit上安装jdk1.8
  2. api数据加密的定义_云原生时代,如何构建开箱即用的数据加密防护?
  3. 对于U盘做系统,win8降级到win7系统的心得体会
  4. phpcmsV9SQL注射+列目录
  5. 安卓开发-Activity中finish() onDestroy() 和System.exit()的区别
  6. [BOI2019][第K大问题][暴力剪枝]D2T1 Olympiads
  7. JavaScript | 嵌套if的示例
  8. js的parseInt函数结果为0很奇怪的问题
  9. LeetCode 701 二叉搜索树中的插入操作
  10. java软件的安装过程
  11. 基于Nginx的图片预览或下载
  12. Flink流处理框架下的交通灯控制器
  13. 判断是否为 retina屏幕
  14. PXE+HTTP+TFP+DHCP自动化部署
  15. STM32-IIC模拟从模式
  16. UPS不间断电源测试技巧有哪些?
  17. 图像采集卡 | 以每秒千兆像素的速度进行图像处理
  18. mysql 1100_错误代码:1100 Table 't_depart_info' was not locked with LOCK TABLES的解决方法
  19. 我所玩过的GALGAME——メンアットワーク!魔兽学院men at work2的中文版《永远的羁绊》全攻略...
  20. 拼多多笔试 公司套餐

热门文章

  1. c++ 字母降落小游戏
  2. js把HTML转成对象,将js对象转换为html
  3. adf的主要功能之一是_复印机ADF是什么意思
  4. flash activex java_Adobe flash player ActiveX和NPAPI和PPAPI 这三个软件有什么区别?哪个是不必要的?...
  5. 赛博朋克2077 夜曲余烬电梯无法进入+保镖堵门bug解决方法
  6. 【VUE3】Hash模式与HTML5模式使用区别
  7. 2016年9月16日-乔任梁逝世,陈乔恩发文悼念
  8. 研究生留学资助项目突遭暂停,美国两大科研机构设限,上千访问学者或受影响...
  9. 什么是云计算?云计算概念集合
  10. 咱也来谈谈web打印快递单及经验