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

news2024/12/30 2:43:57
任务描述

本关任务:八数码问题是在一个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 **********#

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Dev-C++读取输入直到文件结束 while (scanf(“%s“, str[num]) != EOF)没反应的情况

问题描述&#xff1a; Dev-C编译cpp文件读取输入直到文件结束 while (scanf("%s", str[num]) ! EOF)没反应 如图&#xff1a; 下列代码要实现的功能是从标准输入中读取一系列字符串&#xff0c;然后将这些字符串以相反的顺序输出到标准输出中。 #include <bit…

Java项目:74 ssm基于Java的超市管理系统+jsp

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 功能包括:商品分类&#xff0c;供货商管理&#xff0c;库存管理&#xff0c;销售统计&#xff0c;用户及角色管理&#xff0c;等等功能。项目采用mave…

Visual Basic6.0零基础教学(4)—编码基础,数据类型与变量

编码基础,数据类型与变量 文章目录 编码基础,数据类型与变量前言一、VB中的编程基础二、VB的基本字符集和词汇集1、字符集2、词汇集 VB中的数据类型VB中的变量与常量一.变量和常量的命名规则二.变量声明1.用Dim语句显式声明变量三. 常量 运算符和表达式一. 运算符 1. 算术运算符…

Kotlin协程CoroutineScope命名空间CoroutineName,Kotlin

Kotlin协程CoroutineScope命名空间CoroutineName&#xff0c;Kotlin import kotlinx.coroutines.*fun main(args: Array<String>) {val myName CoroutineName("fly")runBlocking {CoroutineScope(Dispatchers.IO).launch {repeat(3) {val name coroutineCont…

代码随想录训练营Day32:● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 题目链接 https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/ 题目描述 思路 看完视频讲解之后豁然开朗啊简直了&#xff01;&#xff01;&#xff01; 统计后一天减去前一天&#xff0c;差值为正数的&#xff0c;再…

Java前缀和

一维前缀和&#xff1a; public class Main {private static final int N 100010;public static void main(String[] args) {int[] s new int[N];int[] a new int[N];int n 10;// 定义10个数for (int i 1; i < n; i) {a[i] (int) (Math.random() * 10);}for (int i 1…

小白如何兼职赚得第一桶金?六大网络赚钱方式让你轻松开启副业之旅

小白如何兼职赚得第一桶金&#xff1f;六大网络赚钱方式让你轻松开启副业之旅 无需担忧&#xff0c;以下是一些精心挑选的线上兼职建议&#xff0c;将助你迅速开启赚钱之旅。 1&#xff0c;参与网络调查&#xff1a;各大市场调研公司及品牌商常常需要了解消费者心声&#xff0c…

【QA】MySQL导出某数据库的所有数据为sql文件,包含建库命令、建表命令。

文章目录 前言Windows系统下 | mysqldump导出数据库数据Docker中导入初始化数据【补充】通过命令行&#xff0c;执行sql文件&#xff0c;将数据导入到数据库在MySQL外面执行在MySQL中执行 前言 我们在用docker部署mysql项目的时候&#xff0c;往往需要对数据库进行数据初始化。…

蓝桥杯java---螺旋矩阵

解题思路&#xff1a; int [][] arr new int[n][m];int i 0, j -1, temp 1;while (n * m > 0){for (int p 0; p < m; p)//从左自右arr[i][jj1] temp;n--;if (n * m 0) break;for (int p 0; p < n; p)//从上自下arr[ii1][j] temp;m--;if (n * m 0) break;fo…

【机器学习300问】44、P-R曲线是如何权衡精确率和召回率的?

关于精确率和召回率的基础概念我已经写了两篇文章&#xff0c;如果友友还不知道这两个评估指标是什么&#xff0c;可以先移步去看看这两篇文章&#xff1a; 【机器学习300问】25、常见的模型评估指标有哪些&#xff1f;http://t.csdnimg.cn/JtuUO 总结一下这两个概念&a…

单片机LED流水灯的演变过程

文章目录 前言一、代码的重要三部曲二、使用步骤1.LED 过程 三、几种流水灯方式1.循环移位2.数组3.移位运算符“>>”、“<<”4. 位操作 四、效果图总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 课程需要&#xff1a; 提示&#xff1…

获取Book里所有sheet的名字,且带上超链接

应用背景&#xff1a; 当一个excel有很多sheet的时候&#xff0c;来回切换sheet会比较复杂&#xff0c;所以我希望excel的第一页有目录&#xff0c;可以随着sheet的增加&#xff0c;减少&#xff0c;改名而随时可以去更新&#xff0c;还希望有超链接可以直接跳到该sheet。 可以…

EDR下的线程安全

文章目录 前记进程断链回调执行纤程内存属性修改early birdMapping后记reference 前记 触发EDR远程线程扫描关键api&#xff1a;createprocess、createremotethread、void&#xff08;指针&#xff09;、createthread 为了更加的opsec&#xff0c;尽量采取别的方式执行恶意代…

Web自动化测试:如何生成高质量的测试报告

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 运行了所有测试用例&#xff0c;控制台输入的结果&#xff0c;如果很多测试用例那也不能够清晰快…

C语言----strcpy和strcat的使用和模拟实现

一&#xff0c;strcpy()函数 strcpy() 函数是 C语言中一个非常重要的字符串处理函数&#xff0c;其功能是将一个字符串复制到另一个字符串中。该函数原型如下&#xff1a; char*strcpy(char*dest,const char*src) 其中&#xff0c;dest 表示目标字符串&#xff0c;即将被复制到…

AttributeError: ‘_MSDataLoaderIter‘ object has no attribute ‘_put_indices‘

问题描述 复现代码过程中遇到错误&#xff1a;AttributeError: _MSDataLoaderIter object has no attribute _put_indices 解决方案 出错的原因是代码中使用了不存在的属性"_put_indices"。这个错误可能与你使用的版本不兼容有关。在pytorch1.x版本中&#xff0c;&q…

江苏开放大学2023年春《液压与气压传动060246》第2形考作业占形考成绩的25%参考答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 电大搜题 多的用不完的题库&#xff0c;支持文字、图片搜题&am…

Unable to authenticate, need: BASIC realm=“Sonatype Nexus Repository Manager“

问题 使用公司的私有源&#xff0c;执行 npm i 的时候突然报错了&#xff1a; 解决 执行命令 npm config list找到 .npmrc 去掉对应的这一行即可&#xff0c;或者使用 nrm 工具执行 nrm del xxx 删掉私有源&#xff0c;然后在添加私有源也可。可以参考我这篇&#xff1a;使…

七个项目管理经典案例分析

案例一 赵晓东是海正公司的员工&#xff0c;最近感到很烦恼。公司签下了一份价值超过100万的合同&#xff0c;由于双方老板关系密切&#xff0c;都希望项目能够尽快启动&#xff0c;所以在签订合同时没有举行正式的签字仪式。合同签署后&#xff0c;公司老总立即指定赵晓东和其…

【C++】虚拟继承 组合

目录 一、虚拟继承 &#x1f31f;【非虚拟内存分布】 &#x1f31f;【虚拟继承内存分布】 &#x1f31f;【虚拟继承读取】 &#x1f31f;【练习检验】 &#x1f31f;【继承的总结和反思】 二、组合 &#x1f31f;【继承和组合】 &#x1f31f;【前言回顾】 上一篇文章我们…