概述
众所周知,人工智能在近些年十分火爆,在各个领域的应用也是十分广泛,其中一个领域就是导航。
也许有人会说,导航不就是电子地图嘛?和人工智能有什么关系?但其实在导航中人工智能发挥了很重要的作用。“条条大路通罗马”,为什么导航会向你推荐这条路而不推荐另一条路?这其中就涉及到了人工智能中的搜索算法。
在搜索中,往往有初始状态(initial states)、目标状态(goal states),而人工智能所做的,就是在众多情形中,找到众多解决方法的最优解,其中会涉及到循环和迭代,假如我们现在有一个初始状态,我们要怎样达到目标状态呢?
概念介绍
在这之前需要先了解一些概念:
- 动作:通常是接受状态的函数,返回在此状态中应该做出的决策
- 迁移模型:通常是接受状态和动作的函数,返回在此状态下做出此种决策后的新状态
- 节点:数据结构,用来记录状态,父节点,动作和路径成本
- 边界:数据结构,用来区分已探索的部分和未探索的部分
算法讨论
在了解了相关概念之后,我们开始讨论算法:
- 首先将初始状态放入边界,然后进入循环
- 如果边界为空,则无解
- 从边界中移去一个节点
- 如果此节点包含目标状态,则追踪父节点,然后返回解决方案
- 扩张节点并将其加入边界
下面举个例子:
假设我们现在的要求是找到从A到E的路径
- 第一步我们将A(初始状态)放入边界,进入循环
- 因为A不是目标状态,将A移出边界并将B加入边界
- 因为B不是目标状态,将B移出边界并将C和D加入边界
- 因为C不是目标状态,将C移出边界并将E加入边界
- 因为E是目标状态,所以我们返回从A到E的路径,循环结束
那么在实际情况下是怎么样的呢?
实战
下面进入实战环节:
情境:你现在有一张迷宫地图,起点为A,终点为B,需要编写程序令计算机自行找出离开迷宫的路并显示在屏幕上
1 | # # # # # B |
我们将这个待实现的程序按照之前所讨论的分为几个部分:
如图所示,我们主要将程序分为三个部分:
首先是定义相关的变量,并且将初始状态放入边界;
然后进入循环:
- 第一步:检查边界的长度,如果等于0则说明无解
- 第二步:从边界中移去一个节点,并将此节点添加到表示已探索的数据结构
- 第三步:检查被移去的节点,如果此节点包含目标状态,则追踪其父节点,并返回路径
最后,我们使用一个循环来追踪完整的路径,并将其显示在屏幕上。
以下是根据思路实现的代码:
1 | map1 = [['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'], |
在实现的过程中,我遇到了一些问题:
- 由于对Python中类的使用不熟练,导致代码中有关部分看起来很奇怪
- 在遍历当前节点周围的有效节点时,没有重新初始化节点,而是直接将当前节点周围有效节点的坐标加入边界,导致循环从第二次开始就无法进行,因为坐标没有“周围的有效节点”这个属性
- 在将以探索过的节点加入对应数据结构后,检查下一个节点是否在此数据结构时直接使用“==”,但是即使坐标相同的两个节点也可能不相等,原因是这两个节点存在于不同的地址,正确的做法是再遍历一遍存储以探索过的数据结构,直接比较此数据结构中节点的坐标值
在完成初步的实现后,处于对代码重构的要求,对代码做了相关的函数封装:
1 | map1 = [['#', '#', '#', '#', '#', '#', '#', '#', '#', '#'], |
结果如下:
1 | # # # # # B |
现在,这个程序已经能够找出各种迷宫地图的解法了,也就是说,如果我们对地图进行修改,程序也能够正常运行并输出相应结果,比如,我们将地图的唯一道路“封死”,那么结果就会是“No result”,如下所示:
1 | # # # # # B |
至此,我们就完成了对此情境下相关代码的编写。
修改于2021.04.11