A*算法思想容易理解,但要想设计出好的A*算法,则必需要全面深入了解它。在本文章中接下来的内容中,将全面深入探讨该话题。如果对该算法还没有理解的话,则请先查阅上篇文章《A*算法入门》,然后再看该文章。

一:理论篇

探讨:估值函数

A*算法之所以效率高是因为它是启发式的搜索算法。它是在Dijkstra算法的基础上,增加了书籍网路信息的评估,也就是增加了约束条件,从而改变算法的走向 -------- 即:是带有目的性的往目标节点逼近,而不是你Dijkstra算法那样盲目搜索。因此,A*算法的执行效率高低在非常大的程度上是依赖于估值函数的,估值函数构造的越准确,则A*搜索的时间越短。

估值函数的主要任务是预估待搜索节点的重要程度。其定义为从初始节点经过当前节点到达目标节点的最小代价路径的代价估计值。公式:f(n) = g(n) + h(n),f(n)即为估值函数,g(n) 为初始节点到当前节点的已知代价,h(n) 为当前节点到目标节点的预估函数,其实就是所谓的启发函数。其实如果网路点确定的话,h(n)的值也是确定的,关键就是要看h(n)到底是选择什么样的构造函数。不同的h(n),其值肯定不同,从而影响到f(n)的值。

构造的启发函数h(n),不能与实际最短路径相差太远。差的越远,则A*算法的搜索就越接近BFS,特别地,当h(n) = 0时,就完全退化为BFS了。因此搜索的时间就越来越高。因此,如果启发函数能构造成与实际最短路径完全一样的话,则理论上解搜索速度是最快的。但实际中,这是不可能的,因为它只是预估函数。

在实际中,如果想要尽量快速,就要尽量将该函数构造的越接近实际最短路径,而要做到这点,则必需要参考更多的启发信息量。如:当前待考察节点与目标节点的“关系”或系统中的其他一些信息量,比如:经过该当前待考察节点到目标节点的权重等等。但如此一来h(n)的计算量也会随之增加,因此,这是一种权衡的诀择。下面介绍几种觉的启发函数。

探讨:常见的启发函数

以下假设当前待考察节点为n1(x1, y1),目标节点为n2(x2, y2)。

A:曼哈顿距离

曼哈顿距离其实是一种城市街区距离,即:类似城市街区那样,计算两个路口间的距离是按这两个路口坐标的水平差量与垂直差量的总和来算的。因此:h(n1) = abs(x2 - x1) + abs(y2 - y1)。曼哈顿距离计算公式经试验是较为适合街区类型地图的。

B:对角距离

如果地图允许往对角方向运动的话,则曼哈顿距离是需要考虑8个方向的。因此为简化计算,可以简单使用4个方向来代替。对角距离只取曼哈顿距离的其中差距较大的那个信息。因此:h(n1) = max(abs(x2 - x1), abs(y2 - y1))。

C:欧几里德距离

如果地图是允许往任意方向运动的话,则可以考虑使用两点间的直线距离。因此:h(n1) = sqrt(abs(x2 - x1)^2 + abs(y2 - y1)^2)。

在实际构造启发函数时,如果有可能还可以考虑待考察节点的朝向信息。即:考虑待考察节点(也就是当前节点)朝向是否与起始节点往目标节点朝向吻合。朝向越稳合,则说明该节点越应该被重视。因此,不妨可以为启发函数设定一个系数。节点的朝向越吻合,则系数越低;朝向越不吻合,则系数越高。这样启发函数的启发信息参考的就越多。

二:实现篇

探讨:开表、闭表

启发函数的不同构造,是在整体上影响着算法的路径选择走向,从而影响算法的整体性能。一旦启发函数明确后,则算法的整体走向是确定的,之后影响算法的效率问题就落在算法实现层面。这其中开表与闭表的设计就显的尤为重要,为何?(不明白为何的人,请再次查阅上一篇《A*算法入门》篇,直到看懂为止)。

开表在算法中的作用不单单只是为了存储待考察的节点,在整个算法动作过程中,需要频繁取出(开)表中f(n)值最小的那个,以及需要频繁确认当前节点的邻接节点是否已落在在开表中。而闭也也是需要频繁被查阅是否当前节点的邻接节点已经在闭表中。因此,开表、闭表的性能十分重要。此处只论开表,如果开表设计好了,自然了解如果设计闭表。

方案一:链表(不可取)

使用链表可以在存储上得到便利,而定位f(n)值最小节点时,效率奇低,因而绝对不可取。

方案二:平衡二叉搜索树(可取,综合性能很好,但还不是最好的)

如果应用该方案,则一般情况下会优先考虑rb-tree。该结构是被大量试验证明,其在插入、查找、删除方面性能都很高的一种数据结构,而且许多语言都有对它进行设计实现。

方案三:(小根)堆(可取,性能很好,但只限在定位或提取f(n)值最小节点方面)

在定位或提取f(n)值最小节点方面,堆方案绝对会比二叉搜索树来的高,但其在定位当前节点的邻接节点是否已在表中时,则只能遍历表数据,这方面性能就大不如方案二。

在实际中,个人还是更推荐堆方案。之前设计的C++版本的A*算法,采用的便是堆方案。在88 x 88无掩码地图格上,任意两点间的寻路耗时都不超过4ms。而且该实现版本是适用于任何类型地图的。(其实该版本还是有可优化的空间的,因为在估值函数方面,本人没有做任何优化。)

其实A*算法在存储开销上,也是需要重点注意的,不同的设计者,设计方案不同,效率肯定也不一样,此处不讨论。Ok,今天到此为止吧,有兴趣的同学,欢迎共同探讨。

A*算法深入的更多相关文章

  1. B树——算法导论(25)

    B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...

  2. 分布式系列文章——Paxos算法原理与推导

    Paxos算法在分布式领域具有非常重要的地位.但是Paxos算法有两个比较明显的缺点:1.难以理解 2.工程实现更难. 网上有很多讲解Paxos算法的文章,但是质量参差不齐.看了很多关于Paxos的资 ...

  3. 【Machine Learning】KNN算法虹膜图片识别

    K-近邻算法虹膜图片识别实战 作者:白宁超 2017年1月3日18:26:33 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...

  4. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  5. 散列表(hash table)——算法导论(13)

    1. 引言 许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表 在介绍散列 ...

  6. 虚拟dom与diff算法 分析

    好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM

  7. 简单有效的kmp算法

    以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...

  8. 神经网络、logistic回归等分类算法简单实现

    最近在github上看到一个很有趣的项目,通过文本训练可以让计算机写出特定风格的文章,有人就专门写了一个小项目生成汪峰风格的歌词.看完后有一些自己的小想法,也想做一个玩儿一玩儿.用到的原理是深度学习里 ...

  9. 46张PPT讲述JVM体系结构、GC算法和调优

    本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...

  10. 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法

    若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...

随机推荐

  1. Intersection - POJ 1410(线段与矩形是否相交)

    题目大意:给一个线段和一个矩形,判断线段是否和矩形有公共点.   分析:用矩形的四个边当线段判断与所给的线段是否有交点,需要注意的是给的矩形是不标准的,需要自己转换,还需要注意线段有可能在矩形内部. ...

  2. nyoj 102 次方求模【快速幂】

    次方求模 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 求a的b次方对c取余的值   输入 第一行输入一个整数n表示测试数据的组数(n<100)每组测试只有一 ...

  3. iOS 应用程序本地化

    由于iPhone,iPad等苹果产品在全世界范围内的广泛流行,那么通过App Store下载应用程序的用户也将是来自世界范围的人们,所以开发者在开发过程中势必要考虑到不同语言环境下用户使用,好在iOS ...

  4. Javac编译和JIT编译

    编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行: 其中绿色的模块可以选择性实现.很容易看出,上图中 ...

  5. thread.wait的一个好例子

    int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); HelloThread thread; thread.star ...

  6. EXCEL 如何将多个工作表或工作簿合并到一个工作表

    在使用Excel 时,我们经常需要将多个工作表或工作簿合并到一个工作表中,这样我们就能快速地对数据进行分析和统计.对于一般用户而言,除了复制每个工作表后再粘贴,没有其他什么方法了.如果只是合并少数几个 ...

  7. hdu 2457 DNA repair

    AC自动机+DP.按着自动机跑,(其实是生成新的满足题目要求的串,然后找改变最少的.)但是不能跑到是单词的地方,如果跑到单词的话那么说明改变后的串含有病毒了,不满足题意.然后就是应该怎么跑的问题了,现 ...

  8. SPOJ DQUERY 求区间内不同数的个数 主席树

    这题跟HDU3333差不多吧. 离线的做法很简单,不再说了 以前做过. 主席树的做法就比较暴力了.. 什么是主席树呢.. 其实是某种称号. 在该题中的体现是可持久化的线段树. 对于一个数 如果以前没出 ...

  9. mysql 参数:[read_buffer_size] [sort_buffer_size] [read_rnd_buffer_size] [tmp_table_size]---图解

    http://imysql.cn/2008_09_27_deep_into_mysql_sort_buffer http://my.oschina.net/realfighter/blog/36442 ...

  10. 了解Unicode编码

    制定Unicode编码标准的组织有两个,一个是国际标准化组织ISO,一个是多语言软件制造商组成的统一码联盟. 通用字符集UCS(Universal Character Set)是由ISO制定的编码方案 ...