在现实世界的应用中,搜索问题无处不在。搜索算法对于简化或解决诸如数据库或互联网搜索等问题至关重要。其中最受欢迎的搜索问题之一是在一个网格上找到两个节点之间的最短路径。许多类似谷歌地图的有趣应用都使用智能搜索算法来执行请求,以找到两个城市或地点之间的最优化路径。本文讨论了所有从基础到最先进的算法,这些算法被用于像谷歌地图这样的应用中,以解决类似于寻找最短路径的搜索问题。
首先定义了在网格中找到两个节点之间最短路径的问题。然后讨论了构成最强大的智能搜索算法A*算法基础的算法,包括深度优先搜索(DFS)、广度优先搜索(BFS)和贪婪最佳优先搜索(GBFS)。尝试理解完整的A*算法,这是搜索问题中最强大和智能的算法之一,同时涵盖了基础算法的概览。最后,通过讨论一些关键思想来结束本文,这些思想使得A*成为寻找最短路径的最优化搜索算法,并讨论了A*算法的变体。
在这些算法中,一部分搜索算法与寻找最短路径相关。搜索算法被用来构建AI游戏玩家,通过使用对抗性算法(这可以在即将到来的文章中涵盖)来玩井字棋或国际象棋等游戏。
寻找两个网格节点之间的最短距离或路径是一个普遍的搜索问题。高效的算法被嵌入到谷歌地图等服务中,以根据用户的输入估计最优路径。问题陈述的非正式定义是,给定网格/地图中的两个节点/单元格A和B,找到从A到B的最短最优路径。
为了在网格上找到两个单元格之间的最短路径(下图所示),一些基础的搜索算法被分类为两种类型:有信息的和无信息的。无信息算法不利用问题陈述中的信息,类似于DFS和BFS;然而,DFS并不保证任何最优解。有信息算法利用问题陈述中的信息,以计算成本或时间方面更优化的方式解决问题,类似于GBFS和A*算法。其中一个非常有效的算法,最常用于在地图上找到两个单元格或地点之间的最短路径,是A*算法。
深度优先搜索(DFS)是一种图遍历算法,用于以深度优先的方式遍历图。换句话说,深度优先搜索算法探索一个特定节点到完全深度,直到它到达一个死胡同,只有在返回时才探索另一条可用路径。DFS通常使用先进后出栈实现。DFS算法描述如下:
1. 从某个任意节点开始,并将节点推入一个空栈。
2. 在一个while循环中,从栈中弹出一个节点。
3. 展开节点,将邻居推入栈中,并探索特定节点直到到达死胡同。
4. 一旦到达死胡同,从栈中弹出顶部节点并开始探索该节点。
5. 在这种遍历中,如果找到了目标或目标,就找到了解决方案。否则,得出没有解决方案的结论。
广度优先搜索(BFS)是DFS的一个有效替代方案,它保证了一个准确的解决方案,但不一定是最优的。BFS使用先进先出队列数据结构来遍历节点图。以下算法描述了实现细节:
1. 从一个任意节点开始,并将节点推入一个空队列。
2. 在一个while循环中,从队列中弹出一个节点。
3. 展开节点,将邻居推入队列,并探索特定节点直到结束。
4. 一旦到达死胡同,从队列中弹出顶部节点并开始展开和探索该节点。
5. 在这种遍历中,如果遇到了目标或目标,就找到了解决方案。否则,没有找到解决方案。
贪婪最佳优先搜索(GBFS)是BFS的扩展,它还使用问题陈述中的信息来有效地做出路径选择决策。GBFS为网格上的每个节点关联了一个成本,确定当前节点离目标状态有多远。与节点相关联的成本可以以多种方式计算;一些常用的距离启发式包括:
1. 欧几里得距离
欧几里得距离通常用于在样本空间是任何方向的情况下选择路径。
cost = sqrt((curr_node.x – target.x) ^2 + (curr_node.y – target.y) ^2)
2. 曼哈顿距离
曼哈顿距离通常用于在四个正交方向中选择路径(像棋盘上的大象)。
cost = abs(curr_node.x – target.x) + abs(curr_node.y – target.y)
在编码示例中,考虑曼哈顿距离作为启发式,并继续选择具有最低估计成本的节点。GBFS遵循与BFS相同的步骤,但它选择了一个成本效益高的节点,而不是BFS,后者选择了队列顶部的节点。
A*算法是一种基于启发式的搜索算法,它是从贪婪最佳优先搜索发展而来的,其中算法使用目标单元格的位置数据来谨慎地选择路径。A*是GBFS的更好版本,它使用额外的启发式函数来选择路径。
A*使用两个启发式来有效地决定路径选择,一个是用来计算当前单元格到目标的距离,类似于GBFS,另一个是跟踪从起始单元格到当前单元格的距离。使用这两个启发式可以释放出选择更好路径选择的力量,因为第一个启发式指导选择更近的路径。相比之下,第二个启发式确保当前路径短且不会导致绕路或内部曲线。通过这种方式,这两个启发式最终引导算法不仅要找到解决方案,还要以优化的成本方法找到解决方案。