【算法】搜索算法—盲目搜索和启发式搜索

搜索算法

本文主要以一些概念对较为常见的搜索作简单介绍:

一、盲目搜索


    对一个图进行搜索意味着按照某种特定的顺序依次访问其顶点。在所有搜索方式中,广度优先算法和深度优先搜索算法都十分重要,因为它们提供了一套系统地访问图数据结构的方法。我们着重讲解广度优先搜索算法。

具体例子可看以下文章:

广度和深度解析

1.深度优先搜索

深度优先搜索算法(简称DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点的所在边都己被探寻过,搜索将回溯到发现节点的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。由于深度优先搜索不是接下来最短路径算法的基础,因此这里不做拓展。

2.广度优先搜索

广度优先搜索算法(简称BFS)又称为宽度优先搜索从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点。
在执行算法的过程中,每个点需要记录达到该点的前一个点的位置 —父节点。这样做之后,一旦到达终点,便可以从终点开始,反过来顺着父节点的顺序找到起点,由此就构成了一条路径。
以上两种算法的不同搜索策略可以通过下面网页查看动图,这是两种相邻节点之间的移动代价相等时用到的算法,图中的边不设权值。
https://cs.stanford.edu/people/abisee/tutorial/bfsdfs.html

3.Dijkstra算法

Dijkstra算法是由计算机科学家Edsger W.Dijkstra在1956年提出的。
考虑这样一种场景,在一些情况下,图形中相邻节点之间的移动代价并不相等。例如,游戏中的一幅图,既有平地也有山脉,那么游戏中的角色在平地和山脉中移动的速度通常是不相等的。在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。
对比了不考虑节点移动代价差异的广度优先搜索与考虑移动代价的Dijkstra算法,又如下动图所示:

可以看出当图形为网格图,并且每个节点之间的移动代价是相等的,那么Dijkstra算法将和广度优先算法变得一样。以下网址链接可以自行设置绿色网格的位置。

在这里的无法插入图片描述

Dijkstra算法(可自行设置障碍物)

二、启发式搜索算法

1.贪婪最佳优先

在Dijkstra算法中,我已经发现了其最终要的缺陷,搜索存在盲目性。在这里,我们只针对这个痛点,采用贪婪最佳优先搜索来解决。如何解决?我们只需稍微改变下观念即可,在Dijkstra算法中,优先队列采用的是,每个顶点到起始顶点的预估值来进行排序。在贪婪最佳优先搜索采用的是,每个顶点到起始顶点的预估值来进行排序。
两者的搜索过程对比如下动图所示:

在这里插入图片描述

明显看到右边的算法(贪婪最佳优先搜索 )寻找速度要快于左侧,虽然它的路径不是最优和最短的,但障碍物最少的时候,他的速度却足够的快。这就是贪心算法的优势,基于目标去搜索,而不是完全搜索。
贪婪最佳优先搜索动态图(可自行设置障碍物)

2.A star算法

我们找到了最短路径和搜索顶点最少数量的两种方案,Dijkstra 算法和贪婪最佳优先搜索。接下来能否汲取两者的有点选择既速度快又能得到最优解的算法?.
A star算法正是这么做了,它吸取了Dijkstra 算法中的当前代价,为每个边长设置权值,不停的计算每个顶点到起始顶点的距离,以获得最短路线,同时也汲取贪婪最佳优先搜索算法中不断向目标前进优势,并持续计算每个顶点到目标顶点的距离,以引导搜索队列不断想目标逼近,从而搜索更少的顶点,保持寻路的最优解。A star算法在运算过程中,每次从优先队列中选取f(n)值最小(优先级最高)的节点作为下一个待遍历的节点。A star算法使用两个集合来表示待遍历的节点,与已经遍历过的节点,这通常称之为open_set和close_set。
A star算法优先队列排序方式基于估价值,估价值由顶点到起始顶点的距离(代价)加上顶点到目标顶点的距离(启发函数)之和构成。
A star算法、贪婪最佳优先、Dijkstra算法三者的静态效果图如下:

在这里插入图片描述

三者动态效果图对比(可自行设置障碍物)

三、启发函数

f(n)=g(n)+h(n)f(n)=g(n)+h(n)
f(n)=g(n)+h(n)

f(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。g(n) 是节点n距离起点的代价。h(n)是节点n距离终点的预计代价,这也就是A star算法的启发函数。
上面已经提到,启发函数会影响A star算法的行为。
在极端情况下,当启发函数h(n)始终为0,则将由g(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
如果h(n)始终小于等于节点n到终点的代价,则A star算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
如果h(n)完全等于节点n到终点的代价,则A star算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
如果h(n)的值比节点n到终点的代价要大,则A star算法不能保证找到最短路径,不过此时会很快。在另外一个极端情况下,如果h(n)相较于g(n)大很多,则此时只有h(n)产生效果,这也就变成了贪婪最佳优先搜索。
由上面这些信息我们可以知道,通过调节启发函数我们可以控制算法的速度和精确度。因为在一些情况,我们可能未必需要最短路径,而是希望能够尽快找到一个路径即可。这也是A star算法比较灵活的地方。对于网格形式的图,有以下这些启发函数可以使用:
如果图形中只允许朝上下左右四个方向移动,则可以使用曼哈顿距离。
如果图形中允许朝八个方向移动,则可以使用对角距离。
如果图形中允许朝任何方向移动,则可以使用欧几里得距离。

总结

以下网页是五种不同搜索算法的搜索过程对比:

五种搜索算法(可进行添加多种障碍物)

下面对在有权图(紫)和无权图(黄)上利用搜索算法的特点做一个总结:

本文相关PDF文件:
Graph Search Algorithms

本文参考文章链接:

http://frankorz.com/2017/12/16/greedy-best-find-search/ https://cs.stanford.edu/people/abisee/tutorial/customize.html https://zhuanlan.zhihu.com/p/54510444?utm_source=com.tencent.tim&utm_medium=social&utm_oi=774664375033163776 https://www.gameres.com/777251.html