Python利用A*算法解决八数码问题

资源下载地址:https://download.csdn.net/download/sheziqiong/86790565
资源下载地址:https://download.csdn.net/download/sheziqiong/86790565

利用 A *算法解决八数码问题

摘要

利用 A 算法解决八数码问题,比较不同启发函数(h1,h2)的搜索效率,并验证关于 A 算法的命题。

导言

本实验主要使用 A*算法解决八数码问题。

八数码问题主要是由 8 个 1-8 的数字以及一个空格组成一个九宫格,通过移动空格若干次使得九宫格中数字到达以下目标状态:

123
84
765

对于每个状态,可以往不同方向移动空格使得该状态产生多个不同的新状态作为后继节点,于是对于候选状态的选择策略可以有 BFS、DFS、启发式搜索等。对于八数码问题,我们可以理解成一种路径搜索问题,以其实状态为起点,目标状态为终点,进行搜索。每个节点的可搜索路径为其空格往四个(或更少)方向移动后得到的、不属于其前驱节点的状态。

实验过程

A 算法即对于候选节点每个节点 n,有 f(n),根据 f(n)排序并选择最优者。其中 f(n)定义为

在这里插入图片描述

其中 g(n)为起始状态到 n 的距离,h(n)为 n 到目标状态的预估距离。

若进一步规定 h(n)≥0,并且定义:

在这里插入图片描述

其中,f*(n)表示起点经过 n 到终点的最优路径搜索费;g*(n)表示起点到 n 的实际最小费用;h*(n)表示 n 到终点的实际最小费用的估计。

当要求 h(n)≤h*(n),就称这种 A 算法为 A*算法。

A*算法解决八数码问题的主要流程如下:

设 S0 为起始状态,Sg 为目标状态:

Open={ S0}

Closed={}

如果 Open={},失败退出

在 Open 表中取出其末尾元素 n,n 放到 Closed 表中

若 n∈Sg,则成功退出。

产生 n 的一切非 n 前驱节点的后继,并构成集合 M。

对 M 中的元素 P,分别作两类处理:

若 P G(G=Open+Closed),说明 P 并未被搜索到过,则将其计算 f(n),并将其插入 Open 表的合适位置,维护 Open 表从大到小的顺序,并将其加入 G 表中。(这里 f(n)=g(n)+h(n),根据不同 h(n)有不同的启发式函数,不同的搜索策略)

若 P∈G,说明 P 已经是别的节点的后序节点,则比较从 n 到 P 以及从 P.father 到 P 的消耗,并决定是否更改 P.father转第三步

在我的代码中,用一个类保存一个状态,每个状态包括一个节点的九宫格信息以及其父节点的指针。即,Tree 的信息跟每个节点的状态存储在一个对象中。

在算法退出后,保存最后得到的目标状态,一路根据其 node.father 追溯到根节点(起始状态),再逆序,即可得到路径。

使用的 f(n)中 h1(n)为九宫格中格子与数字不对应的个数,h2(n)为九宫格中每个格子中数字到正确位置需要消耗(横向纵向移动次数总和),即曼哈顿距离之和。比较两者 Open 表长度和到达目标的步数。

结果分析

运行环境为 windows10,Intel Core i5-8400 2.81GHz,RAM 2667MHz 16GB

编译器 PyCharm,语言 Python3

利用 A*算法求解八数码问题,在输出界面上动态显示 OPEN 表的结点数和评估函值最小的结点

比较两种启发函数(上课和书上讲到的 h1(n)和 h2(n))的搜索效率,在输出界面上动态显示 OPEN 表的结点数、总扩展的结点数和评估函值最小的结点

输出 OPEN 表中在最佳路径上的结点及其评估函数值。

先使用课本上的样例进行测试:

283
164
75
迭代次数h1(n)h2(n)
1
2
3
4
5
6
7结束
路径

观察上表可以看到,h2(n)可以迭代更少次数,而且 Open 表和 G 表讲更短。

随机选取了 5 个可行测例,比较 h1(n)与 h2(n)的效率:

测例h1(n)(迭代数/耗时)h2(n)(迭代数/耗时)
[1, 2, 4];[3, 8, 6];[5, 0, 7]Step: 6034 ;Cost: 9.5 sStep: 928 ;Cost: 0.30 s
[0, 3, 8];[7, 4, 6];[2, 1, 5]Step: 3126 ;Cost: 2.3 sStep: 240 ;Cost: 0.03 s
[7, 6, 3];[8, 2, 1];[0, 5, 4]Step: 3352 ;Cost: 2.5 sStep: 489 ;Cost: 0.09 s
[0, 7, 8];[5, 1, 3];[2, 4, 6]Step: 2968 ;Cost: 1.9 sStep: 150 ;Cost: 0.02 s
[6, 3, 2];[7, 5, 8];[1, 0, 4]Step: 5902 ;Cost: 8.0 sStep: 611 ;Cost: 0.13 s
[2, 7, 0];[8, 4, 1];[3, 5, 6]Step: 38166 ;Cost: 322.7 sStep: 3730 ;Cost: 3.37 s

可以很清晰比较到两者的差距,具有更多信息的 h2 的将更快搜索到目标。

验证凡 A 算法挑选出来求后继的点 n 必定满足: f(n)≤f(S0):

f*(S0)表示实际最小费用,用

在这里插入图片描述

路径长-1 表示。(len(path)表示路径经过的点,减 1 后则为路径边的个数)

随机三个可行测例进行三个测试(使用 h2(n))

f*(S0)f(n)范围
[5, 6, 8], [3, 0, 4], [7, 1, 2]24[18, 24]
[5, 7, 6], [0, 2, 4], [1, 8, 3]25[17, 25]
[2, 4, 6], [7, 3, 1], [0, 8, 5]24[14, 24]

验证 h1(n)的单调性,显示凡 A*算法挑选出来求后继的点 ni 扩展的一个子结点 nj,检查是否满足: h(ni)≤1+h(nj):

在这里插入图片描述

这里使用了课本上的测例,随机测例由于步数过多无法写进报告。因此对于随机测例,我在添加了如下代码:

在这里插入图片描述

对于每个扩展的 P(即 nj)作判断,若有违反命题的则打印。结果没有打印。

如果将空格看作 0,即九数码问题,利用相似的启发函数 h1(n)和 h2(n),求解相同的问题的搜索图是否相同?

不一样。
在这里插入图片描述

当初始起点如左图

把空格看作 0,h1(n)需要迭代步数 6882,h2(n)需要迭代步数 1352而空格不算入 h1(n)和 h2(n)的话,h1(n)需要迭代步数 6507,h2(n)需要迭代步数 1246每次迭代都要取节点并扩展,因此搜索图将是不一样的。

同时它也不是一个 A 算法。假设从目标状态回退一步(即 0 往任意方向与邻居交换),得到的状态的 h1(n)与 h2(n)都是 2,但实际其与目标的距离都是 1,违反了 h(n)<=h(n)的规则。

写出能否达到目标状态的判断方法

将 3*3 的格子以行为顺序排列成一行数组 A。

对于 A 中每个数字 n,定义 f(n)为 A 中数字 n 前面比 n 小的数字的个数。定义 SUM 为 f(1)到 f(8)的总和。

我们可以验证 SUM 的奇偶性将一直保持不变:

当空格左右移动,所有 f(n)都保持不变,SUM 不变

当空格向上移动(设与 n 交换),则在数组 A 中介于空格和 n 中的数有 2 个,记为 a, b。

  • < n and b < n,则 f(n) += 2,SUM += 2
  • < n and b > n,则 f(n) += 1,f(b) -= 1,SUM 不变
  • n and b > n,则 f(a) -= 1,f(b) -= 1,SUM -= 2

当空格向下移动,同理可以发现 SUM 永远以 2 为单位加减变化,其奇偶性不变,因此起点的奇偶性与终点的奇偶性需一样,这是可达的必要条件。

然而其充分性,我没有能力证明。

实际上应该使用 numpy 等提升数组操作的速率,然而发现这点时代码已经成型不方便改动。或者使用 Java,因为该项目不太需要作图。

资源下载地址:https://download.csdn.net/download/sheziqiong/86790565
资源下载地址:https://download.csdn.net/download/sheziqiong/86790565

  • 2
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
A*算法是一种启发式搜索算法,可以用来解决十五数码问题。十五数码问题是一种经典的滑块拼图游戏,其中有一个4x4的网格,上面有编号为1到15的方块,还有一个空方块。目标是通过交换方块,将它们按照从1到15的顺序排列好,最后是一个完全有序的网格。 A*算法的基本思想是将问题抽象为一个图,其中每个节点代表一个局面状态,边表示状态之间的转移。我们使用一个启发函数来评估每个节点的优先级,这个函数考虑到从当前状态到目标状态的距离估计。在十五数码问题中,可以使用曼哈顿距离来评估启发函数,即从当前状态到目标状态所需要的最小水平和垂直移动次数之和。 具体实现A*算法解决十五数码问题的步骤如下: 1. 创建一个优先队列,用于存储待扩展的节点。 2. 将初始状态加入优先队列。 3. 从优先队列中选择优先级最高的节点进行扩展。 4. 生成所有可达的下一状态,并计算每个状态的优先级。 5. 将未扩展的状态加入优先队列。 6. 重复步骤3和4,直到找到目标状态。 在Python中实现A*算法解决十五数码问题可以借助以下数据结构算法: 1. 使用优先队列来存储待扩展的节点,可以使用heapq库实现。 2. 设计一个状态类来表示每个局面状态,包括当前状态、父节点、移动代价、优先级等信息。 3. 利用哈希表来记录已经生成的状态,以避免重复扩展。 4. 实现曼哈顿距离的计算函数来评估每个状态的优先级。 总结来说,借助A*算法的思想和Python数据结构算法库,可以较为高效地解决十五数码问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shejizuopin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值