人工智能(Educoder)-- 搜索技术 -- 启发式搜索

任务描述

本关任务:八数码问题是在一个3×3的棋盘上有1−8位数字随机分布,以及一个空格,与空格相连的棋子可以滑动到空格中,问题的解是通过空格滑动,使得棋盘转化为目标状态,如下图所示。

为了简化问题的输入,首先将空格用数字0表示,然后将3×3的棋盘用9位长的字符串表示,则上图的初始状态为724506831,目标状态为012345678,本关卡所有目标状态均为012345678,也保证初始状态到目标状态有解。

对于上图的初始状态,将数字2移动到空格,称之为u操作(空格上移),将数字3移动到空格,称之为d操作(空格下移),将数字5移动到空格,称之为l操作(空格左移),将数字6移动到空格,称之为r操作(空格右移),则一个合法移动路径为lurdrdllurrdllurrulldrrull。

相关知识

为了完成本关任务,你需要掌握:1.评估函数,2.贪婪最佳优先搜索,3.A*搜索:缩小总评估代价,4.求解思路。

评估函数

在有信息搜索 Informed Search 策略中,常使用的是最佳优先搜索 Best First Search ,它的结点扩展是基于评估函数值f(n)选择的。评估函数被看做是代价估计,因此代价最低的结点最先被选择扩展。

对f(n)的选择决定了搜索策略,大部分的最佳优先搜索算法的f(n)由启发式函数h(n)构成:

h(n)=结点n到目标的最小代价路径的代价估计值

贪婪最佳优先搜索

贪婪最佳优先搜索 Greedy Best-First Search 试图扩展距离目标结点最近的结点,原因是这种策略可能可以非常快的找到解,因此,贪婪最佳优先搜索只使用启发式信息,即f(n)=h(n)。

A*搜索:缩小总评估代价

A* 搜索(A 星搜索)是最广为人知的最佳优先搜索,它对结点n的代价评估结合了g(n),即到达此结点n已经花费的路径代价,和h(n),即从该结点n到目标结点所花代价。

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

由于g(n)是从开始结点到结点n的路径代价,而h(n)是从结点n到目标结点的最小路径代价的估计值因此:

f(n)=经过结点n的最小代价解的估计代价

所以,要寻找最小代价的解,首先扩展的是g(n)+h(n)值最小的结点。可以发现,A* 搜索算法与一致代价搜索算法类似,区别是 A* 搜索算法使用g(n)+h(n)而不是g(n)。

求解思路

该问题是将与空格相连的数字移动到空格的位置上,也就相当于将空格移动到与之相连的位置,因此,以空格为当前结点,扩展结点可能为上下左右四个相连的位置,若使用一般的搜索算法,可能陷入无限搜索中,永远搜不到目标解,而 A* 搜索算法则能非常好的将搜索过程导向求解目标。

问题给的是字符串数据724506831,可以还原成如下形式:

那么空格的l移动操作即为下标4和下标3上所对应的数字的交换,分别为0和5,交换后的新的状态为:

以此类推,空格的lrud各操作均可用以上的交换过程表达。

A* 算法的重中之重就是启发式函数h(n)的设计,不同的设计方法可能产生不同的求解路径。在这里,可以选择欧氏距离作为评估函数值:除0之外,各个数字在当前状态的下标与目标状态的下标的绝对值之和。例如:当前状态为123456780,目标状态为:012345678,数字1的下标分别为0和1,数字2的下标分别为1和2,...,数字8的下标分别为7和8,则当前状态与目标状态的评估值为h(n)=abs(1−2)+abs(2−3)+⋯+abs(7−8)=8。

编程要求

本关的编程任务是补全右侧代码片段 salvePuzzle 、 calcDistH 和 moveMap 中 Begin 至 End 中间的代码,具体要求如下:

  • 在 salvePuzzle 中,根据输入参数init(初始状态,如724506831)和targ(目标状态,均为012345678),实现 A* 搜索算法,返回八数码问题的移动路径,如上图的移动路径:lurdrdllurrdllurrulldrrull。

  • 在 calcDistH 中,计算当前状态(参数srcmap,如724506831)到目标状态(参数destmap,如012345678)的启发式函数值h(n),并返回h(n)。

  • 在 moveMap 中,实现行动转换,并返回下一个状态,例如当前状态为参数curmap=724506831,当前 8 数码状态curmap中空格 0 的位置索引i=4,移动空格到位置j=3,则返回的新状态为newmap=724056831。

测试说明

平台将自动编译补全后的代码,并生成若干组测试数据,接着测试程序会调用上述函数,并判断函数返回的路径是否为合法解,若是则输出 Accepted 表示程序正确,否则程序错误。

以下是平台的测试样例:

测试输入: 724506831

预期输出: Accepted

代码
# -*- coding:utf-8 -*-

class Solution:

    def salvePuzzle(self, init, targ):
        ''' 求解8数码问题
        参数:
        init - 初始状态 例如'123046758'
        targ - 目标状态 均为'012345678'
        返回值:
        clf - 由udlr组成的移动路径字符串
        '''

        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        clf = ''  # 初始化移动路径字符串
        state_open = []  # 初始化开放列表
        state_close = []  # 初始化关闭列表
        state_open.append([init,99,'test',init,0])  # 将初始状态加入开放列表
        fn = 2  # 初始化启发式函数的权重
        flag = 1  # 初始化标志位
        while True:
            cur_state = state_open.pop(0)  # 取出开放列表中的第一个状态
            state_close.append(cur_state)  # 将当前状态加入关闭列表
            if cur_state[0] == targ:  # 如果当前状态等于目标状态
                while 1:
                    clf += cur_state[2]  # 将当前状态的移动方向加入移动路径字符串
                    if cur_state[3] == init:  # 如果当前状态的父状态等于初始状态
                        break
                    for id,item in enumerate(state_close[1:]):  # 遍历关闭列表中的状态
                        if item[0] == cur_state[3]:  # 如果找到父状态
                            cur_state = item  # 更新当前状态为父状态
                return  clf[::-1]  # 返回逆序的移动路径字符串

            i = cur_state[0].find('0')  # 找到空格0的位置索引
            flag = 1  # 重置标志位

            if str(i) not in '036':  # 如果空格0不在第一行、第三行和第六行
                tmp_map = self.moveMap(cur_state[0],i,i-1)  # 尝试将空格0向左移动
                if tmp_map not in [tmp[0] for tmp in state_close]:  # 如果新状态不在关闭列表中
                    for id,item in enumerate(state_open):  # 遍历开放列表中的状态
                        if item[0] == tmp_map:  # 如果找到新状态
                            if item[1] + item[4] > self.calcDistH(tmp_map,targ) + cur_state[4] + fn:  # 如果新状态的代价大于当前状态的代价
                                state_open[id] = [tmp_map,self.calcDistH(tmp_map,targ),'l',cur_state[0],cur_state[4]+fn]  # 更新开放列表中的状态
                                flag = 0  # 设置标志位为0
                                break
                            break
                    if flag == 1:  # 如果标志位为1
                        state_open.append([tmp_map,self.calcDistH(tmp_map,targ),'l',cur_state[0],cur_state[4]+fn])  # 将新状态加入开放列表
            flag = 1  # 重置标志位

            if str(i) not in '258':  # 如果空格0不在第二行、第五行和第八行
                tmp_map = self.moveMap(cur_state[0],i,i+1)  # 尝试将空格0向右移动
                if tmp_map not in [tmp[0] for tmp in state_close]:  # 如果新状态不在关闭列表中
                    for id,item in enumerate(state_open):  # 遍历开放列表中的状态
                        if item[0] == tmp_map:  # 如果找到新状态
                            if item[1] + item[4] > self.calcDistH(tmp_map,targ) + cur_state[4] + fn:  # 如果新状态的代价大于当前状态的代价
                                state_open[id] = [tmp_map,self.calcDistH(tmp_map,targ),'r',cur_state[0],cur_state[4]+fn]  # 更新开放列表中的状态
                                flag = 0  # 设置标志位为0
                                break
                            break
                    if flag ==1:  # 如果标志位为1
                        state_open.append([tmp_map,self.calcDistH(tmp_map,targ),'r',cur_state[0],cur_state[4]+fn])  # 将新状态加入开放列表
            flag = 1  # 重置标志位

            if i-3>=0:  # 如果空格0不在最左边的三列
                tmp_map = self.moveMap(cur_state[0],i,i-3)  # 尝试将空格0向上移动
                if tmp_map not in [tmp[0] for tmp in state_close]:  # 如果新状态不在关闭列表中
                    for id,item in enumerate(state_open):  # 遍历开放列表中的状态
                        if item[0] == tmp_map:  # 如果找到新状态
                            if item[1] + item[4] > self.calcDistH(tmp_map,targ) + cur_state[4] + fn:  # 如果新状态的代价大于当前状态的代价
                                state_open[id] = [tmp_map,self.calcDistH(tmp_map,targ),'u',cur_state[0],cur_state[4]+fn]  # 更新开放列表中的状态
                                flag = 0  # 设置标志位为0
                                break
                            break
                    if flag ==1:  # 如果标志位为1
                        state_open.append([tmp_map,self.calcDistH(tmp_map,targ),'u',cur_state[0],cur_state[4]+fn])  # 将新状态加入开放列表
            flag = 1  # 重置标志位

            if i+3<=8:  # 如果空格0不在最右边的三列
                tmp_map = self.moveMap(cur_state[0],i,i+3)  # 尝试将空格0向下移动
                if tmp_map not in [tmp[0] for tmp in state_close]:  # 如果新状态不在关闭列表中
                    for id,item in enumerate(state_open):  # 遍历开放列表中的状态
                        if item[0] == tmp_map:  # 如果找到新状态
                            if item[1] + item[4] > self.calcDistH(tmp_map,targ) + cur_state[4] + fn:  # 如果新状态的代价大于当前状态的代价
                                state_open[id] = [tmp_map,self.calcDistH(tmp_map,targ),'d',cur_state[0],cur_state[4]+fn]  # 更新开放列表中的状态
                                flag = 0  # 设置标志位为0
                                break
                            break
                    if flag ==1:  # 如果标志位为1
                        state_open.append([tmp_map,self.calcDistH(tmp_map,targ),'d',cur_state[0],cur_state[4]+fn])  # 将新状态加入开放列表

            state_open.sort(key=lambda x : x[1] + x[4])  # 根据代价对开放列表进行排序
        #********** End **********#

    def calcDistH(self, src_map, dest_map):
        '''启发式函数h(n)
        参数:
        src_map  - 当前8数码状态
        dest_map - 目标8数码状态
        返回值:
        clf - 当前状态到目标状态的启发式函数值
        '''

        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        if src_map is None or dest_map is None:
            return 0 

        clf = 0
        for i in range(9):
            clf += abs(int(src_map[i])-int(dest_map[i]))
        return clf
        #********** End **********#

    def moveMap(self, cur_map, i, j):
        '''状态转换(交换位置i和j)
        参数:
        cur_map - 当前8数码状态
        i - 当前8数码状态中空格0的位置索引
        j - 将空格0的位置i移动到位置j,位置j移动到位置i
        返回值:
        clf - 新的8数码状态
        '''

        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        if i>j:
            i,j=j,i
        tmp_i = cur_map[i]
        tmp_j = cur_map[j]
        tmp_map = cur_map[:i]+tmp_j+cur_map[i+1:j]+tmp_i+cur_map[j+1:]

        return tmp_map
        #********** End **********#

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/484838.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单使用Swagger

文章目录 1、介绍2、 使用步骤3、 常用注解 1、介绍 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(https://swagger.io/)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0c;有利于团队协作 接口的文…

数据可视化-ECharts Html项目实战(6)

在之前的文章中&#xff0c;我们学习了如何设置散点图、雷达图。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢数据可视化-ECharts Html项目实战&#xff08;5&a…

软考96-上午题-【操作系统】-文件目录

一、文件目录 1-1、定义 为了实现“按名存取”&#xff0c;系统必须为每个文件设置用于描述和控制文件的数据结构&#xff0c;它至少要包括&#xff1a;文件名、存放文件的物理地址。 这个数据结构称为&#xff1a;文件控制块(FCB)&#xff0c;文件控制块的有序集合称为文件…

flutter3_douyin:基于flutter3+dart3短视频直播实例|Flutter3.x仿抖音

flutter3-dylive 跨平台仿抖音短视频直播app实战项目。 全新原创基于flutter3.19.2dart3.3.0getx等技术开发仿抖音app实战项目。实现了类似抖音整屏丝滑式上下滑动视频、左右滑动切换页面模块&#xff0c;直播间进场/礼物动效&#xff0c;聊天等模块。 运用技术 编辑器&#x…

Web前端Html的表单

表单的关键字&#xff1a; form标签表示一个表单区域 action“后端地址” method“提交数据方式:get/post” input 单行输入框 type“text” 文本 name“定义名称 名字自定义” 向后端提交的键 readonly“readonly” 只读&#xff0c;不可修改&#xff0c;但是可以提交 disab…

Django 三板斧、静态文件、request方法

【一】三板斧 【1】HttpResponse &#xff08;1&#xff09;介绍 HttpResponse是Django中的一个类&#xff0c;用于构建HTTP响应对象。它允许创建并返回包含特定内容的HTTP响应。 &#xff08;2&#xff09;使用 导入HttpResponse类 from django.http import HttpResponse创…

C++ unordered_set和unordered_map

哈希 1. unordered_set/unordered_map1.1 背景1.2 unordered_set1.2.1 特性1.2.2 常用方法 1.3 unordered_map1.3.1 特性1.3.2 常用方法 2. 哈希2.1概念2.2 哈希冲突2.2.1哈希函数2.2.2 解决哈希冲突2.2.2.1 闭散列2.2.2.2 开散列 1. unordered_set/unordered_map 1.1 背景 之…

Rust并发编程thread多线程和channel消息传递

安全高效的处理并发是 Rust 诞生的目的之一&#xff0c;主要解决的是服务器高负载承受能力。 并发&#xff08;concurrent&#xff09;的概念是指程序不同的部分独立执行&#xff0c;这与并行&#xff08;parallel&#xff09;的概念容易混淆&#xff0c;并行强调的是"同…

如何理解OSI七层模型?

一、是什么 OSI &#xff08;Open System Interconnect&#xff09;模型全称为开放式通信系统互连参考模型&#xff0c;是国际标准化组织 ( ISO ) 提出的一个试图使各种计算机在世界范围内互连为网络的标准框架 OSI将计算机网络体系结构划分为七层&#xff0c;每一层实现各自…

存储随笔原创科普视频首播~

一周之前&#xff0c;存储随笔创建了B站账号。小编利用上个周末休息时间专门研究了B站视频录制的各种方案。发现并没有想象的很容易&#xff0c;先花了很长时间准备了一个PPT&#xff0c;再准备演讲大纲&#xff0c;最终磕磕绊绊完成了首期原创视频录制&#xff01; 可能不尽如…

PCB布线中晶振电容、电源大小电容、电源电容的设计细节

嵌入式软硬件爱好者 一张手册走天下。嵌入式单片机/Linux/Openwrt/电子电路技术交流分享。//主打一个技术层面的剑走偏锋&#xff0c;直击众人重视和不重视的重点//专注基础&#xff0c;才能走的更远 晶振电容 晶振旁边的电容在电路设计中不是用于滤波的。实际上&#xff0c;…

中国疆域从古至今版图演变,中国历史各个朝代地图大全

一、图片描述 每个朝代都有数十张地图&#xff0c;朝代疆域全图重点区域地图&#xff0c;图片是JPG格式&#xff0c;都是高清地图&#xff0c;行政名称清晰可见&#xff0c;非常适合喜欢历史的朋友。本套历史朝代地图&#xff0c;大小1.32G&#xff0c;18个压缩文件。 二、图…

ShardingSphere水平分表——开发经验(2)

1. 什么场景下分表&#xff1f; 数据量过大或者数据库表对应的磁盘文件过大。 Q&#xff1a;多少数据分表&#xff1f; A&#xff1a;网上有人说1kw&#xff0c;2kw&#xff1f;不准确。 1、一般看字段的数量&#xff0c;有没有包含text类型的字段。我们的主表里面是不允许有t…

C语言数据结构之归并排序

疏雨池塘见 微风襟袖知 目录 归并排序的介绍 基本思想 时间复杂度分析 ⭐归并排序步骤 空间复杂度分析 代码展示 ✨归并排序的非递归 代码展示 总结&#x1f525; 归并排序的介绍 归并排序&#xff0c;是创建在归并操作上的一种有效的排序算法。算法是采用分治法&#xff…

项目1-加法计算器

1.创建项目 2.导入前端代码 2.1 static包内 2.2 测试前端代码是否有误 显示成功说明无误 2.3 定义用户接口 请求路径&#xff1a;calc/sum 请求方式&#xff1a;GET/POST 接口描述&#xff1a;计算两个整数相加 请求参数: 参数名类型是否必须备注num1Integer是参与计算的第…

瑞萨杯(一)

基础信息 RA6M5&#xff1a;ARM V8架构&#xff0c;24MHz外置晶振&#xff0c;200MHz主频 SCI&#xff08;Serial Communications Interface&#xff09;&#xff0c;意为串行通信接口 参考链接&#xff1a; 【瑞萨RA系列FSP库开发】RASCKeil的环境搭建_瑞萨ra mdk-CSDN博客…

主干网络篇 | YOLOv8改进之在主干网络中引入密集连接卷积网络DenseNet

前言:Hello大家好,我是小哥谈。DenseNet(密集连接卷积网络)是一种深度学习神经网络架构,它在2017年由Gao Huang等人提出。DenseNet的核心思想是通过密集连接(dense connection)来促进信息的流动和共享。在传统的卷积神经网络中,每个层的输入只来自于前一层的输出。而在…

C语言之strsep用法实例(八十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【Python音视频技术】Python音视频技术系列文章2---视频提取音频转换文字

接上一篇文章 【Python音视频技术】玩AI视频创作引发写Python音视频技术系列文章1---视频添加字幕 之前我想在视频中提取音频转换文字&#xff0c; 当时是用 PC剪映专业版搞定的&#xff0c; 详情见 【AI应用】模仿爆款视频二次创作短视频操作步骤 。 这里我准备用pytho…

铁道障碍物检测6种YOLOV8

铁道障碍物检测6种&#xff0c;采用YOLOV8训练&#xff0c;得到PT模型&#xff0c;然后转换成ONNX模型&#xff0c;OPENCV调用 铁道障碍物检测6种YOLOV8