A*算法 整理
A*算法
注:本文引自https://www.cnblogs.com/chxer/p/4542068.html
0.概念及何时用
概念:
A*探路搜索,是指在有障碍物的时候,寻找到达目的地最短路径的搜索。
先介绍一些前置知识助于自己理解A*:
- 启发式搜索:这是对每一个搜索位置进行评估,得到好的位置,再从这个位置进行搜索直到目标。
这样子好处是:剪枝,效率更高。
启发式搜索的关键是如何进行估价。
- 估价函数:从当前节点到目的的预估费用。此估计就是启发式的。在寻路问题与迷宫问题中,通常用manhattan估价函数预估费用。
- A*与一般搜索的关系区别:dfs bfs是A*算法的特例。
对于BFS算法,是从当前节点扩展出的每个节点(未被访问)放进队列,从而进一步拓展。其估价函数h为0,边权为1,无任何启发式信息。DFS的h为0。
- 选取最小估价:选择最小估价的节点,用到最小优先级队列(最小二叉堆),C++ STL中有现有的数据结构priority_queue,需要重载自定义节点操作比较符。
- A*算法评价:理论上时间最优,但空间增长为指数级别。
- IDA*算法:迭代加深A*算法,解决空间指数增长问题,甚至可以不用优先级队列。
1.步骤
绿色为起点,红色为重点,蓝色为障碍物。
我们应注意到,整个区域被划分为正方形网格,而我们的搜索区域是正方形扩散。
那么这是算法第一步:
简化搜索区域
我们将每一个正方形区域用二维数组来储存,二位数组的每个元素代表其位置,二维数组的值可以表示为可以通过以及不可通过。
那么路径的求解就是从绿色方块A到红色方块B的二维数组方块的集合。
我们把路径经过的区域叫节点,不叫方块。因为路径完全可以分割成不是正方形的形状,如六边形,矩形。只不过正方形是最简单的情况。
开始搜索
从A 开始,检查相邻的方格,向外拓展。
具体操作:
-
从A开始,将其放入open_list中,open_list 像一个购物清单。其中的内容可能是最终的路径也可能不是。基本上这是待检查方格的清单。
-
寻找周围可到达方格,也将其加入open_list中,并保存为点A的父方格。父方格的作用很重要,后续会讲到。
-
开启列表删除点A ,加入close_list,这是保存不需要检查的方格
蓝框描边的是在close_list中,旁边扩散的箭头是其父节点。
接下来就是要开始启发式搜索用哪个节点了,这里的关键是路径评分。
路径评分
具体评分的函数写法F=G+H
*G:从起点开始,移动到现节点的移动耗费。比如这里每移动,横竖向10,对角向14(不用根号是为了简化)
*H:现节点移动到终点的预估耗费。这里就被称为启发式,因为他只是一个猜测。预估的办法这里提供一种manhattan估价函数预估费用。即计算从此节点到终点,只走横向竖向的数量总和,再乘以10.(忽略障碍物),请注意这里是估算,非实际值。所以他叫启发式。
那么最后的评分就是两者和。
这是评分结果。
继续搜索
我们从open_list中选择F最低的方格,对其做如下处理:
1.从open_list移除,加入close_list中去。
2.检查所有相邻格子。跳过open_list中的格子以及不可达的格子(墙,地形),将未被跳过的加入到open_list中,然后他们作为此节点的父节点。
3.如果相邻节点中已有在open_list中的,检查通过新路径快,还是从那个已在open_list中的节点的父节点过来快。
如图,既然我们都在open_list中,那我们检查G值,发现新路径是右+下,G的值为20,原路径是只要对角线,G的值为14,所以明显旧路径好。
重复这个过程直至到达终点。
最后确定路线是从终点沿着父节点倒过来就是路径了。
2.总结
1,把起始格添加到开启列表。
2,重复如下的工作:
a) 寻找开启列表中F值最低的格子。我们称它为当前格。
b) 把它切换到关闭列表。
c) 对相邻的格中的每一个?
* 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
* 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
* 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
d) 停止,当你
* 把目标格添加进了关闭列表(注解),这时候路径被找到,或者
* 没有找到目标格,开启列表已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。