【Python 百炼成钢】八数码、九宫格问题

简介: 【Python 百炼成钢】八数码、九宫格问题

🤡 前言🤡


问题产生背景:某天我们正在上着人工智能导论这门专业课,由于他与数学关联性灰常的大,所以听得我们是云里雾里,不知其然,不知其所以然。但是这天老师突然提起了大家一块玩拼图游戏。(八数码问题)这下提起了同学们的兴致,从老师那里得知八数码问题是一道典型的启发式搜索问题、还与广度优先遍历有关。这可极大地激发了我的兴趣。但是课堂上的我们却久久没能拿下以至于老师将其作为课后作业留给了我们。戏剧性的是某天我在备战蓝桥杯的时候,写到了一个九宫格问题,并很轻松的使用广度优先遍历解决了他。但是越看越熟悉越看越相似使我想起了什么,突然我顿悟了九宫格=八数码。下面就将两个问题我的解决思路分享给大家。如有不妥之地,还请大家及时提出,多多包涵。


💟九宫格问题💞



💗问题描述💗


题目描述

如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。

经过若干次移动,可以形成第二个图所示的局面。

图一:

123

456

78

图二:

123

46

789

我们把第一个图的局面记为:12345678.

把第二个图的局面记为:123.46758

显然是按从上到下,从左到右的顺序记录数字,空格记为句点。

本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入

输入第一行包含九宫的初态,第二行包含九宫的终态。

输出

输出最少的步数,如果不存在方案,则输出-1。

样例输入

12345678.

123.46758

样例输出

3


💗问题分析💗


九宫格,对每个位置的索引进行排序应为123456789

对于某点而言,他的左边右边分别为他自己的索引减一、加一

他上面下面的索引为他自己的索引减三、加三。

移动的时候可以视为每一步移动的都是空白的小格子。将每一步移动之前的格子都保留下来。使用广度优先搜索。

为了防止闭环,我们可以将扫描过的情况加入scaned队列,如果某种情况在scaned或者tempqueue中就不予入队

我认为也就是这一点符合了启发式式搜索的基本概念。


3d64176ad5d64fe7a3ee924527080c76.png


💗代码实现💗


import copy
# 初始位置
n1=list(input())
# 目标串
n2=list(input())
# 存储最终结果
ans=0
flag=True
# 扫描过的情况
scaned=[]
# 队列,用于保存临时列表状态。
tempqueue=[]
tempqueue.append([n1,ans])
while tempqueue:
    temp=tempqueue.pop(0)
    t,ans=temp[0],temp[1]
    scaned.append(t)
    index=t.index('.')
    # 向上移
    if index//3==0:
        pass
    else:
        t1=copy.copy(t)
        t1[index],t1[index-3]=t1[index-3],t1[index]
        if t1 not in scaned and t1 not in [i[0] for i in tempqueue]:
            tempqueue.append([t1,ans+1])
        if t1==n2:
            print(ans+1)
            flag=False
            break
    # 向下移
    if index//3==2:
        pass
    else:
        t1=copy.copy(t)
        t1[index],t1[index+3]=t1[index+3],t1[index]
        if t1 not in scaned and t1 not in [i[0] for i in tempqueue]:
            tempqueue.append([t1,ans+1])
        if t1==n2:
            print(ans+1)
            flag=False
            break
    # 向左移
    if index%3==0:
        pass
    else:
        t1=copy.copy(t)
        t1[index],t1[index-1]=t1[index-1],t1[index]
        if t1 not in scaned and t1 not in [i[0] for i in tempqueue]:
            tempqueue.append([t1,ans+1])
        if t1==n2:
            print(ans+1)
            flag=False
            break
    # 向右移
    if index%3==2:
        pass
    else:
        t1=copy.copy(t)
        t1[index],t1[index+1]=t1[index+1],t1[index]
        if t1 not in scaned and t1 not in [i[0] for i in tempqueue]:
            tempqueue.append([t1,ans+1])
        if t1==n2:
            print(ans+1)
            flag=False
            break
if flag:
    print(-1)


💟八数码-带路径💞


💙问题描述💙


与上述一样,只不过本次打印出了移动的路径


9f1a274162b04afab1d6ff043bfcf481.png

e3ed002b91f546578ad6ec55da818d8b.png


💙问题分析💙


主体与上述分析一样,只不过这次将该情况之前的路径加了进去(方便最后打印)

8b265e9ae6f8448eb18e41c2a71cb2e9.png


💙代码实现💙


import copy
# 最终判断是否找到了路径(如果没有找到最后打印-1)
flag=True
# 扫描过的队列
scaned=[]
# 队列,用于保存临时列表状态。
tempqueue=[]
# 判断当前情况是否需要入队(n1实际是一个字符列表)
# scaned 是记录的是扫描过的情况
# [i[0] for i in tempqueue]是仍在队列中的情况
# 这么做的目的就是防止形成环路陷入死循环。
def JudgeStrList(t1):
    if t1[0] not in scaned and t1[0] not in [i[0] for i in tempqueue]:
        tempqueue.append(t1)
def PrintRoute(n1,n2):
        temp=''' | |\n V V\n  V '''
        if n1[0]==n2:
            n1[1].append(n1[0])
            print("到达指定的目标需要移动",len(n1[1]),"次!")
            print("移动路径如下:")
            flag=False
            for i in n1[1]:
                if not flag:
                    flag=True
                else:
                    print(temp)
                for j in range(9):
                    print(i[j],end=" ")
                    if j%3==2:
                        print()
            return True
        return False
# 初始位置
n1=list(input())
# 目标串
n2=list(input())
tempqueue.append([n1,[]])
while tempqueue:
    # print(tempqueue)
    temp=tempqueue.pop(0)
    scaned.append(temp[0])
    index=temp[0].index('.')
    # print(index)
    # 向上移
    if index//3==0:
        pass
    else:
        t1=copy.deepcopy(temp)
        t1[0][index],t1[0][index-3]=t1[0][index-3],t1[0][index]
        t1[1].append(temp[0])
        JudgeStrList(t1) 
        if PrintRoute(t1,n2):
            flag=False
            break
    # 向下移
    if index//3==2:
        pass
    else:
        t1=copy.deepcopy(temp)
        t1[0][index],t1[0][index+3]=t1[0][index+3],t1[0][index]
        t1[1].append(temp[0])
        JudgeStrList(t1) 
        if PrintRoute(t1,n2):
            flag=False
            break
    # 向左移
    if index%3==0:
        pass
    else:
        t1=copy.deepcopy(temp)
        t1[0][index],t1[0][index-1]=t1[0][index-1],t1[0][index]
        t1[1].append(temp[0])
        JudgeStrList(t1) 
        if PrintRoute(t1,n2):
            flag=False
            break
    # 向右移
    if index%3==2:
        pass
    else:
        t1=copy.deepcopy(temp)
        t1[0][index],t1[0][index+1]=t1[0][index+1],t1[0][index]
        t1[1].append(temp[0])
        JudgeStrList(t1) 
        if PrintRoute(t1,n2):
            flag=False
            break
if flag:
    print(-1)


💟有解无解判断💞


在以上两个例子中,直接进行了搜索,如果最后搜索不到的话就打印-1。对于无解的情况搜索量极大可能需要耗费大量的时间。我们可以利用线性代数的一些概念先进行有解无解的判断,然后再进行搜索。这样在某种情况下可以节约大量的时间


如果此初始状态的数列(矩阵)的逆序数与目标状态的数列(矩阵)的逆序数的奇偶性一样,则此问题有解。

逆序数的定义:


有一个数列,在这个数列中任意取一对数字(两个数字),其两个数字在数列中的(从前到后)

顺序与数字数值的大小相反,称其为一个逆序。这个数列中所有的逆序的和为逆序数。



💟总结💞


八数码问题就是典型的广度优先搜索,博主代码写的比较啰嗦大家可能看不是很懂。一定要记住不变的本质,每次搜索前先将该情况的所有下一个状态保存,然后在对下一个状态逐个进行搜索。为了避免形成闭环(也就是同一状态重复搜索)我们需要再建立一个数组存放遍历过的状态。然后就是按部就班的搜索了。如果需要打印路径的话,博主想的是对每一种状态的之前的状态进行保留(存进列表)。肯定有更好的方法,欢迎大家评论区留言呀。


相关文章
|
Python
【Python】七段数码管绘制
【Python】七段数码管绘制
1149 0
【Python】七段数码管绘制
|
6月前
|
存储 索引 Python
python图片九宫格图片处理
本篇文章介绍了一个Python项目的实现,项目能够处理图片并将其组合成九宫格或四宫格,同时还具备音乐播放功能,对于初学者来说是一个可以进行实战学习的初级项目。
|
6月前
|
算法 小程序 Python
python制作九宫格切割工具
python制作九宫格切割工具
53 1
|
Python
【用python的标准库画出显示实时时间的数码管】
【用python的标准库画出显示实时时间的数码管】
|
存储 Python
技巧 | Python制作朋友圈炫酷九宫格图片
技巧 | Python制作朋友圈炫酷九宫格图片
python绘制一个时间的七段数码管实例基本的七段数码管绘制
python绘制一个时间的七段数码管实例基本的七段数码管绘制
|
算法 Shell
[oeasy]python0106 七段数码管_显示字母_BP机
[oeasy]python0106 七段数码管_显示字母_BP机
213 0
 [oeasy]python0106 七段数码管_显示字母_BP机
|
芯片
[oeasy]python0105_七段数码管_7_SEGMENT_数码管驱动_4511
[oeasy]python0105_七段数码管_7_SEGMENT_数码管驱动_4511
173 0
[oeasy]python0105_七段数码管_7_SEGMENT_数码管驱动_4511
|
Python
Python生成九宫格图片
Python生成九宫格图片
145 0
|
存储 小程序 计算机视觉
不到100行代码 Python制作一个九宫格图片生成器,炫酷朋友圈!
本文将介绍如何用 Python 将一张图片转化为 9 宫格,并加入 GUI 界面,封装成一个程序,先看一下程序的预览效果:
不到100行代码 Python制作一个九宫格图片生成器,炫酷朋友圈!