A*算法深入
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*算法深入的更多相关文章
- B树——算法导论(25)
B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...
- 分布式系列文章——Paxos算法原理与推导
Paxos算法在分布式领域具有非常重要的地位.但是Paxos算法有两个比较明显的缺点:1.难以理解 2.工程实现更难. 网上有很多讲解Paxos算法的文章,但是质量参差不齐.看了很多关于Paxos的资 ...
- 【Machine Learning】KNN算法虹膜图片识别
K-近邻算法虹膜图片识别实战 作者:白宁超 2017年1月3日18:26:33 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- 红黑树——算法导论(15)
1. 什么是红黑树 (1) 简介 上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...
- 散列表(hash table)——算法导论(13)
1. 引言 许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表 在介绍散列 ...
- 虚拟dom与diff算法 分析
好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- 神经网络、logistic回归等分类算法简单实现
最近在github上看到一个很有趣的项目,通过文本训练可以让计算机写出特定风格的文章,有人就专门写了一个小项目生成汪峰风格的歌词.看完后有一些自己的小想法,也想做一个玩儿一玩儿.用到的原理是深度学习里 ...
- 46张PPT讲述JVM体系结构、GC算法和调优
本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...
- 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法
若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...
随机推荐
- Nginx+Tomcat的服务器端环境配置详解
这篇文章主要介绍了Nginx+Tomcat的服务器端环境配置详解,包括Nginx与Tomcat的监控开启方法,需要的朋友可以参考下 Nginx+tomcat是目前主流的Javaweb架构,如何让ngi ...
- UBUNTU 札记(53条经验)
adobe_pdf 菜单栏 /etc/profile 是一个global config file,会影响系统全局用户,如果你只想对single user生效的话,可以修改vi ~/.bash_prof ...
- ecshop检验邮件是否合法
<?php /** * 验证输入的邮件地址是否合法 * * @access public * @param string $email 需要验证的邮件地址 * * @return bool */ ...
- SQL Server 2008 常见异常收集(持续更新)
写在前面: 最近,在使用SQL Server 2008时,出现了不少问题.发现,很多问题都是以前碰见过的,并且当时也寻找到了解决方法(绝大部分来源于“百度”与“Google”),只是时间一长,又忘记了 ...
- [React + webpack] hjs-webpack
You can easily spend hours configuring the perfect dev environment with all the latest hotness like ...
- Java多线程,哲学家就餐问题
问题描述:一圆桌前坐着5位哲学家,两个人中间有一只筷子,桌子中央有面条.哲学家思考问题,当饿了的时候拿起左右两只筷子吃饭,必须拿到两只筷子才能吃饭.上述问题会产生死锁的情况,当5个哲学家都拿起自己右手 ...
- innodb结构解析工具---innodb_ruby
1.下载ruby并安装ruby: ftp://ftp.ruby-lang.org/pub/ruby/ ftp://ftp.ruby-lang.org/pub/ruby/ruby-2.3-stable. ...
- MapReduce最佳成绩统计,男生女生比比看
上一篇文章我们了解了MapReduce优化方面的知识,现在我们通过简单的项目,学会如何优化MapReduce性能 1.项目介绍 我们使用简单的成绩数据集,统计出0~20.20~50.50~100这三个 ...
- 通过MultipleOutputs写到多个文件
MultipleOutputs 类可以将数据写到多个文件,这些文件的名称源于输出的键和值或者任意字符串.这允许每个 reducer(或者只有 map 作业的 mapper)创建多个文件. 采用name ...
- JAVA异常的捕获与抛出原则
在可能会出现exception的地方,要使用try-catch或者throws或者两者都要.我的判断依据是:如果对可能出现的exception不想被外部(方法的调用者)知道,就在方法内部try-cat ...