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. 实现自己的脚本语言ngscript之四:代码生成

    最近的进度 ngscript测试代码 function c1(a, b, c, d) { this.a = 1; this.b = new array(); this.b[0] = 1; this.b ...

  2. InfoSphere BigInsights 安装部署

    InfoSphere BigInsights 有三个版本:基础版.企业体验版.企业版.基础版是免费的,但是少了一些功能:企业体验版是在购买企业版之前又来体验测试的:如果要部署企业版,应该购买企业版.安 ...

  3. JVM内存管理和JVM垃圾回收机制

    JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...

  4. 使用Codis搭建redis集群服务

    转(http://www.jianshu.com/p/f8e968e57863) 一. 应用场景 redis 作为数据结构存储引擎,有着很多优点 高性能单机引擎可以达到5-10W qps 数据结构全面 ...

  5. 【基础】多线程更新窗体UI的若干方法

    一.前言 在单线程中设置窗体某个控件的值很简单的事,只需要设置控件文本的值就可以了,但是有的业务场景很是复杂,界面上的控件也很多,这种情况下当数据量比较多的时候,在单线程中更新UI不可避免地会发生假死 ...

  6. c#基础语言编程-编码

    字符编码是计算机技术的基础理论,其字符编码有ASCII码.UTF-8.还有就是GB2312,当然这是在中国常用的. 1.ASCII码 在计算机内部所有的信息都是以二进制字符进行存储.用每个二进制位中的 ...

  7. Linux 操作系统位数(32or64)识别

    Linux 操作系统位数识别: LINUX 32位操作系统:Linux x86   i586  i386  i686 i... LINUX 64位操作系统:Linux x64x86_64  X64 . ...

  8. winform 五子棋 判断输赢 分类: WinForm 2014-08-07 20:55 256人阅读 评论(0) 收藏

    新手上路,高手勿进! 利用数组,根据新旧数组值的不同,获取那个点是什么棋子: 说明: 棋盘:15*15; 定义4个全局变量: string[,] stroldlist = new string[15, ...

  9. Mean Shift具体介绍

    Mean Shift,我们 翻译为“均值飘移”.其在聚类,图像平滑.图像切割和跟踪方面得到了比較广泛的应用.因为本人眼下研究跟踪方面的东西,故此主要介绍利用Mean Shift方法进行目标跟踪,从而对 ...

  10. docker-compose 工具安装

    centos 7 安装 之前测试过相关安装方法 pip python 安装不行 还是用下面这个办法搞定 curl -L https://github.com/docker/compose/releas ...