定义

A*算法,A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。

定义解析

  • A*算法是一个“搜索算法”,实质上是广度优先搜索算法(BFS)的优化。从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点。
  • A*算法的作用是“求解最短路径”,如在一张有障碍物的图上移动到目标点,以及八数码问题(从一个状态到另一个状态的最短途径)
  • A*算法的思路类似图的Dijkstra算法,采用贪心的策略,即“若A到C的最短路径经过B,则A到B的那一段必须取最短”,找出起点到每个可能到达的点的最短路径并记录。
  • A*算法与Dijkstra算法的不同之处在于,A*算法是一个“启发式”算法,它已经有了一些我们告诉它的先验知识,如“朝着终点的方向走更可能走到”。它不仅关注已走过的路径,还会对未走过的点或状态进行预测。因此A*算法相交与Dijkstra而言调整了进行BFS的顺序,少搜索了哪些“不太可能经过的点”,更快地找到目标点的最短路径。另外一点,由于H选取的不同,A*算法找到的路径可能并不是最短的,但是牺牲准确率带来的是效率的提升。

例子问题描述

举两个有代表性的例子,以便读者更形象化地理解A*算法

最短路径

中间蓝色是障碍物,求从绿色到红色的最短路径

八数码

在九宫格里放在1到8共8个数字还有一个是空格,与空格相邻的数字可以移动到空格的位置,问给定的状态最少需要几步能到达目标状态(用0表示空格):

关键内容——启发函数

计算出组成路径的方格的关键是下面这个等式(启发函数):

这里,

G = 从起点 A 移动到指定方格的移动代价,沿着到达该方格而生成的路径。

H = 从指定的方格移动到终点 B 的估算成本。这个通常被称为试探法,有点让人混淆。为什么这么叫呢,因为这是个猜测。直到我们找到了路径我们才会知道真正的距离,因为途中有各种各样的东西 ( 比如墙壁,水等 ) 。

G来源于已知点信息,H来源于对未知点信息的估计,F为选择下一个将遍历节点的依据。

此外,H还有一个特征:

  • 在极端情况下,当启发函数h(n)始终为0,则将由g(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
  • 如果h(n)始终小于等于节点n到终点的代价,则A*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
  • 如果h(n)完全等于节点n到终点的代价,则A*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
  • 如果h(n)的值比节点n到终点的代价要大,则A*算法不能保证找到最短路径,不过此时会很快。
  • 在另外一个极端情况下,如果h()n相较于g(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。

由上面这些信息我们可以知道,通过调节启发函数我们可以控制算法的速度和精确度。因为在一些情况,我们可能未必需要最短路径,而是希望能够尽快找到一个路径即可。这也是A*算法比较灵活的地方。

对于网格形式的图,有以下这些启发函数可以使用:

  • 如果图形中只允许朝上下左右四个方向移动,则可以使用曼哈顿距离(Manhattan distance)。计算从当前万格横可或纵回移动到达目标所经过的方格数。
  • 如果图形中允许朝八个方向移动,则可以使用对角距离。横纵移动和对角移动都是合法的。为提高效率,常取整数作系数10,14.
  • 如果图形中允许朝任何方向移动,则可以使用欧几里得距离(Euclidean distance)。两点直线距离。

GitHub代码

https://github.com/while-TuRe/A-star-ShortestPath(用vscode即可运行)

算法思路

开始搜索(Starting the Search)

一旦我们把搜寻区域简化为一组可以量化的节点后,就像上面做的一样,我们下一步要做的便是查找最短路径。在 A* 中,我们从起点开始,检查其相邻的方格,然后向四周扩展,直至找到目标。

我们这样开始我们的寻路旅途:

1. 从起点 A 开始,并把它就加入到一个由方格组成的 open list( 开放列表 ) 中。这个 open list 有点像是一个购物单。当然现在 open list 里只有一项,它就是起点 A ,后面会慢慢加入更多的项。 Open list 里的格子都是下一步可以到达的(当然可能是退回某点后下一步到达),在最终最短路径中,open list中的格子可能会是沿途经过的,也有可能不经过。基本上 open list 是一个待检查的方格列表。

2. 查看与起点 A 相邻的方格 ( 忽略其中墙壁所占领的方格,河流所占领的方格及其他非法地形占领的方格 ) ,把其中可走的 (walkable) 或可到达的 (reachable) 方格也加入到 open list 中。把起点 A 设置为这些方格的父亲 (parent node 或 parent square) 。当我们在追踪路径时,这些父节点的内容是很重要的。因为它记录了从起点到该点的最短路径上经过的最后一个节点。

3. 把 A 从 open list 中移除,加入到 close list( 封闭列表 ) 中, close list 中的每个方格都是现在不需要再关注的。

4. 下一步,我们需要从 open list 中选一个方格,重复23步骤。但是到底选择哪个方格好呢?具有最小 F 值的那个。

所以,我们的路径是这么产生的:反复遍历 open list ,选择 F 值最小的方格,产生新的可供选择的方格,直到找到终点方格。这个过程稍后在“继续搜索”详细描述。我们还是先看看怎么去计算上面的等式。

计算启发函数

如上所述, G 是从起点A移动到指定方格的移动代价。H是从指定方格移动到终点的估计代价。

G的计算思路类似图的Dijkstra算法,采用贪心的策略,即“若A到C的最短路径经过B,则A到B的那一段必须取最短”,找出起点到每个可能到达的点的最短路径并记录。既然我们是沿着到达指定方格的路径来计算 G 值,那么计算出该方格的 G 值的方法就是找出其父亲的 G 值,然后按父亲是直线方向还是斜线方向加上 10 或 14 。在本例中,横向和纵向的移动代价为 10 ,对角线的移动代价为 14 。之所以使用这些数据,是因为实际的对角移动距离是 2 的平方根,或者是近似的 1.414 倍的横向或纵向移动代价。使用 10 和 14 就是为了简单起见。比例是对的,我们避免了开放和小数的计算。这并不是我们没有这个能力或是不喜欢数学。使用这些数字也可以使计算机更快。稍后你便会发现,如果不使用这些技巧,寻路算法将很慢。

有很多方法可以估算 H 值。这里我们使用 Manhattan 方法,计算从当前方格横向或纵向移动到达目标所经过的方格数,忽略对角移动,然后把总数乘以 10 。之所以叫做 Manhattan 方法,是因为这很像统计从一个地点到另一个地点所穿过的街区数,而你不能斜向穿过街区。重要的是,计算 H 是,要忽略路径中的障碍物。这是对剩余距离的估算值,而不是实际值,因此才称为试探法。

把 G 和 H 相加便得到 F 。我们第一步的结果如下图所示。每个方格都标上了 F , G , H 的值,就像起点右边的方格那样,左上角是 F ,左下角是 G ,右下角是 H 。

继续搜索(Continuing the Search)

为了继续搜索,我们从 open list 中选择 F 值最小的 ( 方格 ) 节点,然后对所选择的方格作如下操作:

1.       把它从 open list 里取出,放到 close list 中。

2.       检查所有与它相邻的方格,忽略其中在 close list 中或是不可走 (unwalkable) 的方格 ( 比如墙,水,或是其他非法地形 ) ,如果方格不在open lsit 中,则把它们加入到 open list 中。

把我们选定的方格设置为这些新加入的方格的父亲。

3.       如果某个相邻的方格已经在 open list 中,则检查这条路径是否更优,也就是说经由当前方格 ( 我们选中的方格 ) 到达那个方格是否具有更小的 G 值。如果没有,不做任何操作。

以下是代码的关键部分,简化细节便于读者理解。完整代码在GitHub上可供下载参考,用vscode即可运行。如果对您有用请点个赞吧~

https://github.com/while-TuRe/A-star-ShortestPath

def SearchPath(mapp,cp,target_position):start_time=time.time()#计时board = Board(mapp,target_position)#地图棋盘对象board.GetMsg(cp).IsUnk = 0open_list.append(cp)while(open_list != []):#取出第一个(F最小,判定最优)位置current_position=open_list[0]open_list.remove(current_position)#close_list.append(current_position)用mapx中存储的IsUnk(是否unknow)代替,否则需要在close_list中搜索#到达if(current_position == target_position):print("成功找到解")按要求输出解return#将下一步可到达的位置加入open_list,并检查记录的最短路径G是否需要更新,记录最短路径经过的上一个点#斜(上下左右与此思路相同,只是细节有差)for i in [current_position.x-1,current_position.x+1]:for j in [current_position.y-1,current_position.y+1]:if(IsInBoard(i,j)):new_G=board.GetMsg(current_position).G+14#维护当前已知最短G,如果未遍历或需更新if(board.mapx[i][j].IsUnk): board.mapx[i][j].IsUnk=0open_list.append(Position(i,j))board.mapx[i][j].parent=current_positionboard.mapx[i][j].G=new_Gif(board.mapx[i][j].G>new_G):board.mapx[i][j].parent=current_positionboard.mapx[i][j].G=new_G#对open_list里的内容按F的大小排序open_list.sort(key=lambda elem : board.GetMsg(elem).GetF())

八数码问题

while-TuRe/A-star-8-Puzzel-Problem: A*八数码问题 (github.com)

八数码问题与最短路径搜索问题的不同主要在于以下几点:

  • 目标状态可能无法到达(将棋盘展开按照逆序数的奇偶性进行判断。空格左右移逆序数不变,上下移逆序数变化2)
  • close_list不可被替换,open_list中存储的是棋盘的状态(一个3*3的list)
  • G表示已走过的步数,H表示每个数字棋子当前位置与目标位置曼哈顿距离的和。

如果您觉得有用,请点亮小心心♥吧~码字不易,多谢鼓励~

A*算法详解一看就懂(python)相关推荐

  1. python协程详解_彻底搞懂python协程-第一篇(关键词1-4)

    任何复杂的概念或系统都不是凭空出现的,我们完全可以找到它的演化历程,寻根究底终会发现,其都是在一系列并不那么复杂的简单组件上发展演化而来! by 落花僧 本文通过一系列关键概念,逐步递进理解协程. 0 ...

  2. 【最短路径Floyd算法详解推导过程】看完这篇,你还能不懂Floyd算法?还不会?...

    简介 Floyd-Warshall算法(Floyd-Warshall algorithm),是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似.该算法名称以 ...

  3. [Network Architecture]DPN(Dual Path Network)算法详解(转)

    https://blog.csdn.net/u014380165/article/details/75676216 论文:Dual Path Networks 论文链接:https://arxiv.o ...

  4. 字符串匹配之KMP算法详解

    kmp算法又称"看毛片"算法,是一个效率非常高的字符串匹配算法.不过由于其难以理解,所以在很长的一段时间内一直没有搞懂.虽然网上有很多资料,但是鲜见好的博客能简单明了地将其讲清楚. ...

  5. 多重背包O(N*V)算法详解(——使用单调队列)

    多重背包O(N*V)算法详解(--使用单调队列) 多重背包问题: 有N种物品和容量为V的背包,若第i种物品,容量为v[i],价值为w[i],共有n[i]件.怎样装才能使背包内的物品总价值最大? 网上关 ...

  6. Apollo6.0代码Lattice算法详解——Part5: 生成横纵向轨迹

    Apollo6.0代码Lattice算法详解--Part5: 生成横纵向轨迹 0.前置知识 1.涉及主要函数 2.函数关系 3.部分函数代码详解 3.1 lattice_planner.cc中代码部分 ...

  7. 排序算法,最全的10大排序算法详解(Sort Algorithm)

    文章目录 排序算法,最全的10大排序算法详解(Sort Algorithm) 排序算法分类 排序算法稳定性 时间复杂度(time complexity) 1#时间复杂度的意义 2#基本操作执行次数 如 ...

  8. 极限定律 My Algorithm Space AC自动机算法详解

    转载自:http://www.cppblog.com/mythit/archive/2009/04/21/80633.html 首先简要介绍一下AC自动机:Aho-Corasick automatio ...

  9. JDA人脸检测算法详解

    JDA人脸检测算法详解: 第一步: JDA算法原理详解: 作者建立了一个叫post classifier的分类器,方法如下: 1.样本准备:首先作者调用OpenCV的Viola-Jones分类器,将r ...

最新文章

  1. Java类加载机制详解【java面试题】
  2. Dubbo -- 系统学习 笔记 -- 示例 -- 参数验证
  3. 出现authentication mode=Windows/错误解决办法
  4. aix shell脚本 运行java_Linux中执行shell脚本的4种方法总结
  5. jep(java表达式分析器)简介
  6. 十几位资深架构师,整理了最新架构师学习体系,分享给大家......
  7. 如何查看 Linux 服务器性能参数指标?
  8. 那天有个小孩跟我说LINQ(三)
  9. 基于java的企业进销存管理系统
  10. 整理学习之注意力机制
  11. 微信小程序|基于小程序实现打卡功能
  12. win10开启移动热点,手机无法获取ip地址
  13. javafx 教程_JavaFX,Jigsaw项目和JEP 253
  14. 计算机网络基础之数据链路层的功能与服务
  15. 按照字符串长度大小进行升序排列
  16. A类计算机机房温度变化,机房的温度、湿度标准值是多少 ?
  17. 四扫客户接口——接口测试完成文档
  18. Erlang Introduction(Reproduced)
  19. 小白学习MySQL - 聊聊数据备份的重要性
  20. H3C设备运行状态查询常用命令(建议收藏)

热门文章

  1. 【电脑运用及修理】Google Chrome 浏览器
  2. win7 系统 内存测试软件,win7怎么检测内存 win7系统检测内存的三种方法
  3. Linux菜鸟成长日记 ( vim程序编辑器及常见的文件管理命令)
  4. JAVA开发运维(CI/CD)
  5. windbg- !analyze -v 信息详解
  6. axios跨域携带cookie_axios中cookie跨域及相关配置示例详解
  7. 八年级作文-秦始皇玩电脑
  8. android button设置边框线
  9. vue 打包生产环境报错 UnhandledPromiseRejectionWarning: CssSyntaxError:
  10. 【C语言】杨辉三角常用且简单的两种解法(超详细解说)