我的编程空间,编程开发者的网络收藏夹
学习永远不晚

人工智能实验——八数码难题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

人工智能实验——八数码难题

人工智能实验——八数码难题


人工智能实验——八数码难题

八数码难题简介


八数码问题指的是定义一个3$\times$3的格子,然后把1-8八个数字随机放入这些格子中,然后排列成规则的格子。就像下面图所示:

在这里插入图片描述

而本文所要解决的是,如何设计一个程序解决八数码问题。解决八数码问题其实算是一个搜索问题。

八数码难题所用到的算法简介


  • BFS广度优先搜索算法

    以接近起始节点的程度依次扩展节点的搜索方法 宽度优先搜索是逐层进行的;在对下一层的任一节点进行搜 索之前,必须搜索完本层的所有节点。

    在这里插入图片描述

  • DFS深度优先搜索算法

    优先扩展最新产生的(即最深的)节点的搜索方法 深度优先搜索沿着状态空间某条单一的路径从起始节点向下 进行下去;只有当搜索到达一个没有后裔的状态时,它才考 虑另一条替代的路径。 状态空间搜索树的深度可能为无限深,往往给出一个节点扩 展的最大深度—深度界限

    在这里插入图片描述

  • 贪婪算法(本文未利用)

    贪婪算法的话,它是一种特殊的DFS算法,若设计得不好得话就会直接退化为DFS算法。在这里得话可以利用每个元素到目标点的曼哈顿距离来作为贪婪算法的引导元素。也可以利用在正确位置的元素的个数作为引导。

  • 代价优先算法(本文未利用)

    代价算法的话,一般以当前深度作为代价。优先搜索深度小的。

  • A*算法

    A*算法结合了贪婪算法与代价优先算法,利用两者的和作为代价利用广度优先的数据结构为基础来进行搜索。

在这里插入图片描述

代码实现解释


1.定义八数码类

首先定义了一个八数码类,此类生成的对象是用来存储八数码数据的。八数码类的对象相当于状态空间。此类中包含了四类数据,分别为八数码的排列顺序、父节点、深度、A*算法对应的代价。下面为八数码类。

# 八数码矩阵class Ef:    def __init__(self, value, parent=0, depth=1, hscore=0):        self.value = value        self.parent = parent  # 定义父节点 若不输入父节点 则自定义其为根 也就是说当parent为0时为根        self.depth = depth #深度 默认为1        self.fscore = depth + hscore #A*算法的代价

2.定义操作类

在完成八数码类的编写后,接着的是后继函数即操作。对于后继函数,我的做法是再次定义一个Op类。此类中的函数只有两个,分别为初始化类与空白移动类。在初始化类中,我对传入的八数码对象进行了复制赋值(之所以怎么做是因为Python中的对象类似于指针而不是真正实体)。然后设置了遍历寻找八数码对象中0的位置索引。最后设置了方向参数字典,这样做可以缩减代码量。

在对函数初始化完后,开始编写移动函数,这个函数才是操作类中的主体,这个类的功能主要就是将0与某个方向的数进行替换。一开始编写这个函数时其实分成了四个函数来写的,即上下左右。但是后面发现这些函数的重复性大,于是将不同方向的特定参数整理成了字。将四段代码整理成了一段只要传入字典中的某个值即可移动0。最后返回的是移动成功后的嵌套列表。补充:函数内部拥有一定的边界判别语句,若超界了会返回0。下图为操作类的功能图。

在这里插入图片描述

# 包含所有操作的类 注意python赋值后的对象还是同一个对象 所以要用copyclass Op:    def __init__(self, ef):        self.ef_value = copy.deepcopy(ef.value)        for i in range(0, 3):  # 获取空位置的索引            for j in range(0, 3):                if self.ef_value[i][j] == 0:                    self.j, self.i = j, i                    break        self.direction = {'left': [self.j == 0, 0, -1], 'right': [self.j == 2, 0, 1], \                          'up': [self.i == 0, -1, 0], 'down': [self.i == 2, 1, 0]}  # 方向配置字典    def move(self, direc='left'):  # 交换操作        op = self.direction[direc]        if op[0]:  # 如果在第一列则无法交换 返回0            return 0        else:  # 否则交换            temp = self.ef_value[self.i + op[1]][self.j + op[2]]            self.ef_value[self.i + op[1]][self.j + op[2]] = self.ef_value[self.i][self.j]            self.ef_value[self.i][self.j] = temp            return self.ef_value

3.编写Solution类

完成以上的类的编写后,开始编写程序的主体代码。对于主体,我也还是写了一个Solution类。这个类中的数据主要包含Open表、Closed表、开始状态、目标状态。下图为Solution类各项数据的图解。

在这里插入图片描述

class Solution:    def __init__(self, ef):        self.openT = []  # 定义open表  注意需要用到python自带的队列库        self.openT.append(ef)  # 一开始将其添加进open表        self.closeT = []  # 定义close表        self.start_state = ef  # 定义开始状态        self.des_state = [[1, 2, 3], [8, 0, 4], [7, 6, 5]]  # 定义最终状态        self.des_state_dict = {1:[0,0],2:[0,1],3:[0,2],8:[1,0]   ,0:[1,1],4:[1,2],7:[2,0],6:[2,1],5:[2,2]} #字典格式

初始化完Solution类后,我定义了多个基础的函数,这些函数基本上都是判断函数。包括状态是否到达目标状态判断函数、状态是否重复判断函数、是否可解函数。对于前两个,函数本体基本上都是用推导式一句完成的。而对于是否可解函数相对复杂。此判断首先是把八数码矩阵变成一维的形式,然后从第一个位置开始(除0外)记录每个元素前面比此元素大的个数,并把这些个数相加。如果最后得到的是偶数则有解否则无解。下图为判断函数的代码截图。*参考:*http://t.csdn.cn/0vyzV

    def iSolve(self,ef): #八数码是否有解判断 有解则返回1        #转一维列表        onedi = [ef.value[0][i] for i in range(3)] + [ef.value[1][2]] + \                [ef.value[2][i] for i in range(2, -1, -1)] + [ef.value[1][i] for i in range(2)]        oxe = 0 #计算逆序        for i in range(1, 9):            if onedi[i] != 0:                for j in range(i):                    oxe += 1 if onedi[j] > onedi[i] else 0        return 1 if oxe % 2 == 0 else 0

4.extend函数.

除了以上的基础函数外,还有一个十分重要的基础函数。此函数是用来拓展的。此函数内部包含过滤机制,如果拓展生成的嵌套列表在closed表的话就丢弃,如果不在的话就计算深度并把父节点、深度、代价、嵌套列表传入一个新的八数码对象中,然后添加到open表中。

    def extend(self, ef):  # 拓展节点        ex_direct = ['up', 'down', 'left', 'right']        for d in ex_direct:            temp = Op(ef).move(d)  # 由于这里用到了 copy深度复制  所以就要用一下方法阻止循环            if temp:  # 如果可以上移则执行并且上行的结果不在closeT                for i in self.closeT:  # 这里还可以重构                    if i == temp:                        break                else:                    ef_d = Ef(temp, ef, ef.depth+1, self.h(temp))  # 定义一个新对象 值为返回的上行结果 父节点为ef                    #若要改h(n)在上面改                    self.openT.append(ef_d)

在这里插入图片描述

5.BFS算法

在一切都铺垫好后开始编写搜索算法,首先写的是BFS算法函数。进入函数后首先开始记录当前时间并把它命定义为start_time。然后判断传入的八数码对象是否有解,若无解则放回“No Solution!”,否则进入下面操作。判断Open表是否为空,若为空则放回”Failure”否则进入下面操作。检测Open表队首是否为目标状态,若不为目标状态则对队首元素进行拓展,并把队首纳入closed表中然后弹出。若已经是目标则遍历目标的父节点直到无父节点。然后打印这些父节点与程序运行时间与节点深度。以下为流程图

在这里插入图片描述

    def BFS(self):  # 广度搜索        start_time = time.time()        if self.iSolve(self.openT[0]):            while self.openT:  # 判断openT是否空表                if self.isDes(self.openT[0]):                    result = []                    pointer = self.openT[0]                    result.append(pointer.value)                    while pointer.parent:                        pointer = pointer.parent                        result.append(pointer.value)                    print(f'当前深度:{self.openT[0].depth}\n程序运行时间:{time.time() - start_time}s'                          f'\n以下为推导过程(BFS)')                    while result:                        print(result.pop())  # 逆序打印                    return "Success"                else:                    self.closeT.append(self.openT[0].value)  # 给closeT添加值                    self.extend(self.openT[0])  # 若不是结果的话则拓展ef                    self.openT.pop(0)  # 出队            else:                return "Failure"        else:            return 'No Solution!'

6.DFS算法

对于DFS其代码与BFS的类似,不同的地方在于其利用的数据结构是栈而不是队列,所以它首先检测的是最后一个元素是否是目标,并且也是首先对最后一个元素进行拓展。除此之外,DFS还对此进行了深度的限制。下图为我制作的程序在DFS上的流程图。

在这里插入图片描述

    def DFS(self,depth=20): #深度搜索 需要传入深度参数 默认为100        start_time = time.time()        if self.iSolve(self.openT[0]):            while self.openT:  # 判断openT是否空表且是否超过深度                if self.isDes(self.openT[-1]): #检测倒数第一个                    result = []                    pointer = self.openT[-1]                    result.append(pointer.value)                    while pointer.parent:                        pointer = pointer.parent                        result.append(pointer.value)                    print(f'当前深度:{self.openT[-1].depth}\n程序运行时间:{time.time() - start_time}s'                          f'\n以下为推导过程(DFS)')                    while result:                        print(result.pop())  # 逆序打印                    return "Success"                else:                    self.closeT.append(self.openT[-1].value)  # 给closeT添加值                    if self.openT[-1].depth < depth: #深度限制                        self.extend(self.openT.pop())  # 若不是结果的话则拓展ef                    else:                        self.openT.pop()            else:                return "Failure"        else:            return 'No Solution!'

7.A*算法

而A*算法大体上的程序结构和DFS与BFS是一致的,但是其利用的是优先队列。每一次拓展后都会根据代价的值对OPEN表进行排序,取代价最小的到队首然后对队首进行判断是否为目标,若为目标则打印结果否则进行拓展并排序。而这里的代价有g(n)与h(n)组成,g(n)是当前状态的深度利用的是DFS中的深度接口,而h(n)则是自己设计的。我设计了两种h(n),第一种是八数码九宫格里面各个元素与目标元素的差异个数。另外一种是各个元素到目标元素的最短距离的距离之和。附件中提供的程序利用的是第一种,第二种在代码中也有但是需要更改下接口。下图为A*算法的流程图。

在这里插入图片描述

    def astar(self):        start_time = time.time()        if self.iSolve(self.openT[0]):            while self.openT:  # 判断openT是否空表且是否超过深度                if self.isDes(self.openT[0]): #检测倒数第一个                    result = []                    pointer = self.openT[0]                    result.append(pointer.value)                    while pointer.parent:                        pointer = pointer.parent                        result.append(pointer.value)                    print(f'当前深度:{self.openT[0].depth}\n程序运行时间:{time.time() - start_time}s'                          f'\n以下为推导过程(A*)')                    while result:                        print(result.pop())  # 逆序打印                    return "Success"                else:                    self.closeT.append(self.openT[0].value)  # 给closeT添加值                    self.extend(self.openT[0])  # 若不是结果的话则拓展ef                    self.openT.pop(0)  # 出队                    self.openT.sort(key=lambda x:x.fscore) #排序 根据fscore排序            else:                return "Failure"        else:            return 'No Solution!'

运行结果显示


以上为程序设计的描述部分。对程序设计完后我对程序进行了一些测试,包括不可解八数码的测试,三种算法的测试。以下为测试结果。图9是不可解八数码的测试,结果返回正确。

在这里插入图片描述

下图分别对应的是A*、BFS、DFS三种算法,三者均是对同一个八数码的测试。由此可以看出三者的运行时间有一定的区别。

在这里插入图片描述

代码附件


源代码下载

https://download.csdn.net/download/weixin_51735061/85375158

程序可视化


在这里插入图片描述

上面的为演示,做法在https://blog.csdn.net/weixin_51735061/article/details/125656323

来源地址:https://blog.csdn.net/weixin_51735061/article/details/124775542

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

人工智能实验——八数码难题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

人工智能的实施为何更难实现

编程学习网:我们几乎每天都能读到有关人工智能及其在医疗行业的应用的最新文章,医疗机构类的客户也总是在问我们人工智能到底是什么,医院是否应该也在这方面采取行动。
人工智能的实施为何更难实现
2024-04-23

C++开发经验分享:C++人工智能编程的实践经验

在人工智能领域中,C++是一种非常常用的编程语言,其优化能力和高效性在处理大规模数据时非常出色。然而,C++开发人工智能应用程序并不是一件容易的事情。在实践经验中,有一些技术和工具可以帮助开发人员更加有效地进行C++人工智能编程。本文将会分
C++开发经验分享:C++人工智能编程的实践经验
2023-11-22

亚马逊网络服务上海人工智能实验室

实验室成立于2019年,位于上海市张江高科技园区。实验室拥有一支由来自全球顶尖人工智能领域的专家组成的团队,致力于研究和开发人工智能技术,探索人工智能在不同行业和领域的应用,包括机器学习、自然语言处理、计算机视觉等领域。实验室的主要研究方向包括深度学习、自然语言处理、计算机视觉等,并且在这些领域取得了一系列的创新成果,如语音识别、图像识别、自然语言生成等。实验室的研究成果在人工智能领域具有广泛的应用前...
2023-10-27

如何使用MongoDB实现数据的实时人工智能功能

如何使用MongoDB实现数据的实时人工智能功能引言:在当今数据驱动的时代,人工智能(Artificial Intelligence, AI)技术和应用正成为许多行业和领域的核心关键。而实现实时的人工智能功能,则对数据库的效率和处理能力提出
2023-10-22

C++ 函数的递归实现:递归在人工智能算法中的作用?

递归函数通过调用自身并在特定条件下返回结果来实现。在人工智能算法中,递归广泛应用于深度优先搜索、动态规划、回溯和神经网络等技术。对于处理复杂问题,递归提供了高效且简洁的解决方案。C++ 函数的递归实现:递归在人工智能算法中的作用引言递归
C++ 函数的递归实现:递归在人工智能算法中的作用?
2024-04-22

使用Python编写并实现一个具备人工智能的聊天机器人(包含代码和步骤)

聊天机器人是一种人工智能,它通过应用程序或消息来模拟与用户的对话。本文我们将使用Pytho的chatterbot库来实现聊天机器人。该库生成对用户输入的自动响应。响应基于库中实现的机器学习算法。机器学习算法使聊天机器人在收集用户响应时更容
使用Python编写并实现一个具备人工智能的聊天机器人(包含代码和步骤)
2024-01-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录