这是我参与更文挑战的第8天,活动详情查看: 更文挑战html
本文正在参加「Java主题月 - Java 开发实战」,详情查看 活动连接git
[TOC]算法
A*算法,A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。markdown
基本概念
- 首先在大学咱们遇到最多的算法Dijkstra、Floyd、广度搜索、深度搜索。关于这些算法咱们之后再慢慢的研究,今天的重点在A*算法上。A*算法是一种启发式算法。与上述几种算法不一样的是A*算法在考虑起始节点的同时还会考虑到目标节点的代价。
- 在A*算法中咱们给每一个节点都定义一些属性。最基本的就是下文提到的三基数-这里的三基数是我本身定义的一个名词。什么叫启发式就是在探索路径的时候既要选择里起始点最近也要考虑到里目标节点的消费问题。
三基值
- 上面的一些概念可能会使你很模糊,这里咱们直接看定义。
F=G+H : 表示一个节点的总消费值;换句话说就是离起始节点和目标节点距离的总和 G : 表示该从其实节点到该节点的消费值; H : 表示从该节点到目标节点的消费值;(这里注意一下,这里的消费值实际上是一个预估值,由于咱们没法判断到目标节点的具体路径,这个H值得获取本文会提供三种方法,其中使用最普遍的是曼哈顿距离)函数
图1oop
三基值计算
常规约定
- 在方格地图中咱们约定横向或者纵向单位消费为10
- 在方格地图中斜向单位消费为14
- 在墙(墙、河流等不可通过的节点统称)角咱们是不能够斜着穿越的,这是常识。实际中咱们每一个移动的物体都是有本身的空间的,以下图这样S-->E的过程S'已经占用了Q(墙)的领域了。
图2post
图3.net
G值计算
- 三基数在上面定义中咱们已经列出了其计算公式,可能有的人看的不是很明白。咱们这里详细解释一下
- 首先咱们先看三基值中的G值,G
表示该从其实节点到该节点的消费值
,这句话的意思从开始节点移动到当前节点须要的实际的消费,这里是须要考虑到不可通过的节点的。以下图S表示起始点,E表示目标节点,N表示当前节点,黑色的表示墙(不可通过的节点集合)。咱们规定在墙角是没法斜着穿过去,在其余地方是能够斜着通过的。有了这个约定,咱们能够算出图3中
- S--->N1 消费10,
- S--->N2消费14,由于从S到N2不是处于墙角。
- 途中N3--->N4就是明显的墙角。因此N3--->N4消费是20。
H值计算方式
- H值和G值做用相反,H值是预估到目标节点的消费值。
- 首先G是针对其实点的,而H值是针对目标节点的
- 其次G值是真实值,而H值是预估值
- 最后G值得计算是容许斜线行走的,可是H值计算只能横向和纵向的结合
复制代码
图4
集合列表
- 在咱们A*寻路的过程当中,咱们须要用到两个集合,一个咱们成为开放集合(OpenList),另一个咱们称之为闭合集合(ClosedList)。
- 举个例子咱们去超市购物,咱们都会推个手推车,把喜欢的东西放进购物车里。最后结算的时候或者是中途咱们会选择价格更实惠的东西替代咱们已经选择的同等产品
- 在A*中咱们也是这样,openList就至关于购物车,咱们会将见到的喜欢的物品加入购物车,可是加入购物车并不必定最后会买,在A*中,咱们会将节点周围可用的节点加入openList,可是并不必定最后须要。在openList添加的过程咱们会慢慢用'更实惠'的节点代替已经选择的相同的节点。在超市最后放到咱们本身的包里才是最后咱们要带回家的东西。在A*算法中加到closedList中才是咱们最后的东西。
寻路解析
本小节图片来源如下文章。其中思想参考源如下文章
- 通过上面的介绍咱们了解了A*中咱们一些约定的定义,理解上面这些定义的基础上咱们下面的流程会很易懂。
初始地图
- 如图5,地图上蓝色方格表示墙,关于墙的定义上面已经解释过了。左边青色的表示S(起始节点),右边的红色节点表示咱们的目标节点。小圆点表示从S到E的最有路径之一。从图5咱们能够看出咱们在墙角没有斜走,而在其余路段上咱们选择斜着走了。下面介绍节点就以图5中的坐标为准。好比起始节点咱们就称之为(2,3)。
-
蓝色方格表示不可通过方格
-
青色的边框的方格表示已经加入openList
-
高亮显示的边框表示添加在closedList
-
青色表示其实节点
-
红色表示目标节点
-


递归寻走
- 首先咱们常规的寻路是不可能出现首尾相同的状况。可是项目中得处理这种状况,若是其实节点和目标节点是地图上的统一节点,那么咱们的路径就是当前节点。
- 选择当前节点周围可用的节点,若是不在openList集合中则分别计算三基数的值而且加入到openList集合中,计算三基数的同事将待加入openList的节点的父节点设置为当前节点。
- 若是已经存在openList集合中且结合中的G值大于当前节点(周围节点之一)的G值,则将当前节点更新到openList集合中。不然不加入也不更新。
- 周围节点选择完以后咱们就把该节点(起始节点)加入closedList中,而后从openList中选择F值最小的节点,在继续重复上面三步骤。
图示流程
图6
图7
- 从图7中咱们能够看出在起始节点(2,3)周围(3,3)这个节点的F值此时在openList中最小,因此此时咱们将(2,3)移除openList并将(2,3)加入到closedList中。此时(2,3)由高亮方格显示表示加入closedList集合,
- (2,3)节点就算是结束了他的使命了,加入了closedList集合中的节点咱们将不会再去考虑,咱们能够认为加入到closedList集合中的点已经成墙了。那么下面咱们从openList中选取F值最小(3,3)为新的起始节点,开始重复上面的流程,可是咱们发现(3,3)的右上、正右、右下都是墙,还有正左(2,3)在closedList集合中。这四点咱们是不用考虑的。那么只剩下
(2,2),(3,2),(2,4),(3,4)这四个点。可是这四个点恰巧有所有在openList中。依照上面的流程咱们得以此比较这四个点和openList集合中对应的点的G值谁大谁小。就一个原则谁小留谁。由图8咱们能够知道,新得到的这四个点的G值均大于openlist里对应的点的G值,因此咱们这里放弃这四个点。他们没有被咱们喜欢,咱们抛弃这些点。这一轮结束咱们将(3,3)加入到closedList集合中。若是新节点G值小于对应的openList中的点的G值得话,咱们就要更新openList集合中的对应的那个点。所谓的更新就是将新节点替换原来那个节点。注意此时新节点和openList对应的那个点出了三基数不一样,还有父节点也不一样了。
- 这里须要解释下为何新节点的G值都会加10呢,那是由于咱们最起始的节点是(2,3),而此时的起始节点是(3,3),好比咱们算(2,2)G值得时候其实是(2,3)--->(3,3)--->(2,2)整个过程的G值。因此是10+14。
图8
- 由图9咱们可以看到此时咱们openList中最小F值应该是(3,4),下面的步骤就是一直重复。
图9
不足之处
- 关键在于估价函数h(n)的选取:估价值h(n)<= n到目标节点的距离实际值,这种状况下,搜索的点数多,搜索范围大,效率低。但能获得最优解。而且若是h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。若是 估价值>实际值,搜索的点数少,搜索范围小,效率高,但不能保证获得最优解
源码
源码下载点我
[//]: A译文: blog.csdn.net/coutamg/art… *[//]:其余最小路径算法:www.cnblogs.com/biyeymyhjob… *[//]:启发式算法:baike.baidu.com/item/%E5%90…