A*算法求解迷宫问题(算法讲解与证明、python实现与可视化)

目录

一、引入

二、具体细节

1、BFS(Breadth First Search)

2、Dijkstra(Uniform Cost Search)

3、启发式(Heuristic search)

4、A*算法

4.1 算法细节

4.2 A与A*算法

4.3 A*算法证明

4.4 算法过程

三、具体实现

1、实验要求

2、代码实现

四、源代码


一、引入

       当我开始学习该算法时,网上已经有了很多优秀的介绍性文章了。如果你看到了文章中出现了以下有着红色星星★紫色×的路径图:

        那么该文章很大概率参考了Red Blob Games(点击跳转)的A*算法教程。该教程是一个很不错的引入教程,有着很生动的交互式图表简单易懂的描述过程。

        以下通过BFS贪婪BFSDijkstra算法作为引入逐步引入A*算法,使得对于A*算法有一个初步的整体了解

        Red Blob Games里面有三张图来说明BFSDijkstraA*算法的的区别,如果有点基础的其实看完就知道个大概了:

        BFS(Breadth First Search ):每一个方向都平等的扩展,因此它的探索轨迹是一圈又一圈的同心圆,像涟漪一样一圈一圈均匀地往外扩展。

BFS算法
BFS算法

        我们知道BFS扩展的时候是对一个节点的所有邻接节点依次扩展,每个节点被扩展的机会是公平的,即各个邻接节点的扩展机会一样,顺序任意。在迷宫问题中表现为,选一个节点的右、上、左、下(不一定要右上左下,你想上下左右都可以)节点加入到队列里面,然后按放进去的顺序从队列取出来节点继续扩展,同样是将这个节点的右、上、左、下节点加入队列。那么在迷宫上展现的过程就是对这个节点距离为1的一圈搜索一遍,然后又搜索距离为2的一圈、距离为3的一圈……

        具体动画效果如下:

        从上面可以看到,对于迷宫问题以及其他路径权重相等的图搜索中,BFS是一种非常有用的算法。BFS一定可以搜索到所有可以到达的节点,它是一种暴力的穷尽查找算法,并且能找到最短路径(前提所有边权相等)。

        Dijkstra算法(Dijkstra’s Algorithm :某些节点(方向、路径)会优先被探索,一般是那些具有更小代价的节点和路径会被优先探索。因为该算法是用来求最短路的,算法正确性要求每次都要选择当前已知的最短路进行探索,因此它的探索轨迹是随机的、不均匀的,就像山脊的等高线图一样。

Dijkstra算法

        Dijkstra相比BFS区别就是此时图的各条边的边权不同了,此时BFS不再适用。

        还是迷宫问题,方块里面的值表示起点★到该方块代价(cost)(可以理解为距离、花费的成本)左图中移动到相邻方块的代价都是1右图中移动到相邻方块的权值视方块颜色而定:移动到棕色■的代价是1,移动到绿色■的代价是5灰色■表示不可穿越的障碍。

左图是BFS,右图是Dijkstra

        左图中因为各个方块代价一致,使用BFS算法,算法会直接穿过绿色区域到达终点×右图中因为方块代价不同,使用Dijkstra算法,算法则会绕过绿色区域到达终点×

        它俩算法的执行动画如下:       

左图为BFS,右图为Dijkstra

        具体BFS和Dijkstra算法过程下面会详细介绍,这里只需要知道他们应用的区别。

        A*算法它优先考虑“似乎”更接近目标的路径。该算法也是不断寻找估计“当前最有潜力”的节点,和Dijkstra算法一样都是不均匀的山脊图。但相比Dijkstra算法杂乱无章山脊轨迹,它是有目的性、方向性的,轨迹扩展方向总是选择更靠近目标的那一侧,下面的图可以看到一直往尖端的一侧伸展,因为在A*算法眼中那里更接近终点。

A*算法

        它是对 Dijkstra 算法的修改,针对单个目标进行了优化Dijkstra的算法可以发现到达所有位置的路径。但是在我们寻路算法中,我们可能只需要寻找到达一个位置的路径,这意味着Dijkstra算法中的一些额外开销是不必要的。A*算法是一种单点对单点的路径寻找算法,就像在LOL中点击小地图的某个位置,系统会自动寻路,得到通往目的位置的一条“白线”,来表示它是最短的路径。

        它会利用自己一些已知的启发信息,合理规划自己的探索方向,避免了Dijkstra的一些盲目性。

二、具体细节

        在这里需要提一点的是,下面讨论的A*算法仅仅作为一种寻路算法,讨论的也是仅限于图中路径的搜索。但其实A*算法不止适用于路径搜索,它是一种启发式的思想,它还可以用于其他问题如八数码问题的求解,只是大多数问题最后化简之后可以归结为图论(或者树)的路径求解,因此我们只需理解路径搜索就能理解整个算法思想。

这是一个使用了A*算法的八数码问题的求解过程,如果我们把每个正方形都看作图的节点,指针看作权重为1的边,那就是一棵树(图的特例)最短路径的求解过程

        这一点正如BFS算法,BFS也不仅仅适用于路径搜索,例如算法题目中常用的暴力穷举,但我们学习该算法的时候也只关心路径的搜索,因为暴力穷举最后实质上也是一棵根节点开始的搜索树

         同时为了方便理解,采用的都是网格图,但是其实应用在所有类型的图结构中都是一样的、正确的。这不难理解,因为网格图最终也可以化成节点+边权为1一般图

        BFS和Dijkstra算法我们再熟悉不过了,事实上不必多讲。我们的重点是A*算法,这里具体展开BFS和Dijkstra的算法过程原因是,一方面是希望大家清楚地知道A*算法是如何得来的;另一方面,是我个人太喜欢这个网站的代码图和交互动画了,真的是百看不厌。

1、BFS(Breadth First Search)

       BFS思想的关键就是不断地扩展边界(frontier)。

       BFS的python代码如下:

BFS最基础的代码

        思路是:

        先创建一个队列(Queue,先进先出:先放进的节点先扩展)frontierfrontier用来存储待扩展的节点,因此初始时需要将起点start★放进frontier

        再创建一个集合(Set,集合的元素无序且不会重复)reachedreached用来存储已经到过的节点,就是我们常命名的visit。

        只要frontier不为空,就从frontier中取出一个元素current进行扩展。对于的所有current邻居 next,只要next不在reached里面(即没有到达过),就把next放进frontier里面,然后放到reached标记为已经到达。

        上面的BFS代码没有构建出一条路径,仅仅告诉了我们如何遍历图上的所有点。因此需要进行修改,记录我们是怎么到达每一个点的路径。

        黄色部分是修改的部分:

改进后的BFS代码,能记录路径信息

        这里用came_from替换了reached

        came_from字典(Dictionary,键值对,一个key对应value)came_from不仅可以表示和reached一样的功能(判断一个节点是否到达过),方法是判断came_from里面是不是存在key为这个节点的键值对;还能记录每个点的前序节点,用came_from[节点i]来记录,这样当找到终点时就能顺着前序节点一直寻找到起点。

        还有一处修改部分是:之前是判断是否在reached里面,不在的话直接放到reached集合里面;现在是判断是否在came_from里面,不在的话存储came_from[该节点的邻居next]为当前扩展的current

         当每个节点都存储了从哪里来的信息后,整张图的情况就是下面这样:

         上面的指示箭头就告诉了前序节点是谁,这样当我们BFS找到终点时,我们就有了足够的信息知道我们是怎么到达终点的,也就能重建这一条路径。方法如下:

根据前序节点信息寻找路径的方法

         从goal开始,顺着came_from存储的前序节点,一个一个地回溯到起点,期间不断将路径放到数组path里面,因为越靠近终点的节点越早放进去,所以存储的path最后存储的是一条从终点到起点的反向路径,最后要将整个path反过来。

        上面的BFS最后会遍历完图里的所有节点,也就是会知道起点到所有节点的最短路径(前提是图上边权都相等,否则不是最短)。但实际上我们一般只需要求到某一个目标节点的路径,因此很多过程是不必要的。原有的BFS一定是所有节点遍历完才终止,而我们现在只需要遍历到目标节点后就可以停下了,故我们可以及时终止

        过程如下,一旦找到目标节点,BFS就停止继续扩展:

BFS找到终点后,不管此时frontier里面还有哪些节点没
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隆华爱读书我不爱读书所以我没书读

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值