深度、宽度优先搜索解决九宫格问题(附python代码)

python解决九宫格问题

    • 展示部分
    • 数据的设计:
    • 宽度优先搜索代码:
    • 深度优先搜索代码

最近课内在学习《人工智能引论》、《数据结构》。
前者讲了Open/closed表、宽度、深度优先搜索策略,后者讲到了队列,两门课都期望解决给定初始状态和目标状态的九宫格问题。

用python,以队列驱动搜索,写了代码。

整体思路如下:

展示部分

用matplotlib绘制带有层级的点,代表搜索过程的状态,为这些点打上矩阵标签,就能得到良好的展示效果了。
深度、宽度优先搜索解决九宫格问题(附python代码)_第1张图片
(未带有标签)

深度、宽度优先搜索解决九宫格问题(附python代码)_第2张图片
(带有标签)
(如图,九宫格用0代表空位,初始状态为:[[2,8,3],[1,6,4],[7,0,5]],目标状态为[[1,2,3],[8,0,4],[7,6,5]])
(目标状态是一个顺时针的1-8)

要达到这样的展示效果,需要为matplotlib提供节点位置(x:由本层节点的个数及当前点在本层的位置决定,y:由节点的层次决定。),及对应该节点的矩阵标签。

数据的设计:

open表/closed表存储了这样的一些节点:每个节点存储两个信息:矩阵、节点层级。

矩阵会发生改变,而节点层级又不唯一,字典要求键不改变且唯一,因此不能采用字典存储。

选择使用二元组存储。即(矩阵,层级)。

此处矩阵是二维列表。

进行主循环时,从open表头部取出节点,如果不在closed表中,则将其可能的后续节点加入open表,加入到头部是深度优先搜索,加入到尾部是宽度优先搜索。
这样,在同层级,不会出现重复的节点,但在不同层级,又允许矩阵相同的节点存在。

代码如下,笔者是新手,如果有误,还望见谅。

宽度优先搜索代码:

#人工智能引论作业:宽度优先搜索探索九宫格走法
#在最后一层的最后一个节点,达到了目标状态
#python==3.7.3
#matplotlib==3.1.0
#numpy==1.16.4

import matplotlib.pyplot as plt
import numpy as np
from copy import deepcopy as dp
#用于matplotlib正常显示中文标签
plt.rcParams['font.sans-serif']=['SimHei']

#矩阵标签的像素大小。
lableSize=8

#获取0的位置,即空的位置
def getZeroPosition(twoArray):
    for  i in range(len(twoArray)):
        for k in range(len(twoArray[i])):
            if twoArray[i][k]==0:
                return i,k
#进行0(空位置)与其他位置的交换:操作符/算子定义
def zeroUp(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    t=myArray[zero_x-1][zero_y]
    myArray[zero_x-1][zero_y]=0
    myArray[zero_x][zero_y]=t
def zeroDown(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    #print(zero_x,zero_y)
    t=myArray[zero_x+1][zero_y]
    myArray[zero_x+1][zero_y]=0
    myArray[zero_x][zero_y]=t
def zeroRight(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    t=myArray[zero_x][zero_y+1]
    myArray[zero_x][zero_y+1]=0
    myArray[zero_x][zero_y]=t
def zeroLeft(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    t=myArray[zero_x][zero_y-1]
    myArray[zero_x][zero_y-1]=0
    myArray[zero_x][zero_y]=t

#初始状态
beginData=[[2,8,3],[1,6,4],[7,0,5]]
#目标状态
endData=[[1,2,3],[8,0,4],[7,6,5]]

#矩阵建立坐标系:坐标原点在矩阵的左上角,以纵向为x轴,横向为y轴。
#节点层次
level=1
#open表
lineList=[(beginData,level)]
#closed表
reList=[]
#主循环:open表不空
while lineList!=[]:
    #避免同层次矩阵重复
    if lineList[0] in reList:
        del lineList[0]
        continue
    else:
        #获得当前节点信息
        thisArray=lineList[:][0][0]
        level=lineList[0][1]
        zero_x,zero_y=getZeroPosition(thisArray)
        #进行4项可能移动,将新节点加入open表尾部
        if zero_x!=2:
            timely=dp(thisArray)
            zeroDown(timely)
            lineList.append((timely,level+1))
            
        if zero_x!=0:
            timely=dp(thisArray)
            zeroUp(timely)
            lineList.append((timely,level+1))         
        if zero_y!=2:
            timely=dp(thisArray)
            zeroRight(timely)
            lineList.append((timely,level+1))
            #reList.append((thisArray,level+1))
        if zero_y!=0:
            timely=dp(thisArray)
            zeroLeft(timely)
            lineList.append((timely,level+1))
            #reList.append((thisArray,level+1))
        #加入closed表
        reList.append(lineList[0])
        #目标状态判定
        if lineList[0][0]==endData:
            break
        #print(lineList)
        #删除当前节点
        del lineList[0]
#两个函数用于得到合适的在图上显示的x坐标
def getXList(array):
    oldLevel=1
    times=0
    xList=[]
    for i in range(len(array)):
        nowLevel=array[i][1]
        if nowLevel==oldLevel:
            times+=1
        else:
            xList.append(times)
            times=1
            oldLevel=nowLevel
    xList.append(times)
    return xList
def getXEList(array):
    xEList=[]
    for i in array:
        for k in range(i):
            xEList.append(20/(i+1)*(k+1))
    return xEList

plt.xlim(xmax=22,xmin=0)
plt.ylim(ymax=7,ymin=0)
plt.xlabel('同层次节点个数 ')
plt.ylabel('层次')
t=getXList(reList)
#x,y存储节点位置,lable存储字符串化矩阵
x=getXEList(t)
y=[]
lable=[]
for i in reList:
    y.append(7-i[1])
    strArray=str(i[0])
    lable.append(strArray[0:11]+'\n'+strArray[11:22]+'\n'+strArray[22:])
    #lable.append("[1,3]")
#绘制节点
plt.scatter(x,y,s=3)
#为节点添加标签
for i in range(len(x)):
    plt.annotate(lable[i],xy=(x[i],y[i]),size=lableSize)
plt.show()

深度优先搜索代码

#人工智能引论作业:深度优先搜索探索九宫格走法
#在最后一层的最后一个节点,达到了目标状态
#python==3.7.3
#matplotlib==3.1.0
#numpy==1.16.4
import matplotlib.pyplot as plt
import numpy as np
from copy import deepcopy as dp
#用于matplotlib正常显示中文标签
plt.rcParams['font.sans-serif']=['SimHei']

#深度优先的最大深度
depth=6
#矩阵标签的像素大小
lableSize=8

#获取0的位置,即空的位置
def getZeroPosition(twoArray):
    for  i in range(len(twoArray)):
        for k in range(len(twoArray[i])):
            if twoArray[i][k]==0:
                return i,k
#进行0(空位置)与其他位置的交换:操作符/算子定义
def zeroUp(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    t=myArray[zero_x-1][zero_y]
    myArray[zero_x-1][zero_y]=0
    myArray[zero_x][zero_y]=t
def zeroDown(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    #print(zero_x,zero_y)
    t=myArray[zero_x+1][zero_y]
    myArray[zero_x+1][zero_y]=0
    myArray[zero_x][zero_y]=t
def zeroRight(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    t=myArray[zero_x][zero_y+1]
    myArray[zero_x][zero_y+1]=0
    myArray[zero_x][zero_y]=t
def zeroLeft(myArray):
    zero_x,zero_y=getZeroPosition(myArray)
    t=myArray[zero_x][zero_y-1]
    myArray[zero_x][zero_y-1]=0
    myArray[zero_x][zero_y]=t

#初始状态
beginData=[[2,8,3],[1,6,4],[7,0,5]]
#目标状态
endData=[[1,2,3],[8,0,4],[7,6,5]]

#矩阵建立坐标系:坐标原点在矩阵的左上角,以纵向为x轴,横向为y轴。

#节点层级
level=1
#open表,元素为二元组:(矩阵,层级)
lineList=[(beginData,level)]
#closed表
reList=[]
#仅存储矩阵的open表
arrayList=[]
#主循环:open表不为空
while lineList!=[]:
    #避免同层次矩阵重复
    if lineList[0][0] in arrayList:
        del lineList[0]
        #print(1)
        continue
    #深度优先,设置最大深度
    elif lineList[0][1]>depth:
        del lineList[0]
        continue
    else:
        #将当前矩阵的所有可能的衍生节点都加入open表,要求进行4个可能移动时,矩阵不变
        #深拷贝复制当前节点,避免列表/列表元素的改变
        thisNode=dp(lineList[0])
        thisArray=lineList[:][0][0]
        #当前节点level
        level=lineList[0][1]
        #当前节点矩阵的0的坐标
        zero_x,zero_y=getZeroPosition(thisArray)
        #进行4项可能移动,将新节点插入open表头部
        if zero_x!=2:
            timely=dp(thisArray)
            zeroDown(timely)
            lineList.insert(0,(timely,level+1))
        if zero_x!=0:
            timely=dp(thisArray)
            zeroUp(timely)
            lineList.insert(0,(timely,level+1))      
        if zero_y!=2:
            timely=dp(thisArray)
            zeroRight(timely)
            lineList.insert(0,(timely,level+1))
            #reList.append((thisArray,level+1))
        if zero_y!=0:
            timely=dp(thisArray)
            zeroLeft(timely)
            lineList.insert(0,(timely,level+1))
            #reList.append((thisArray,level+1))
        #将当前节点加入closed表
        reList.append(thisNode)
        arrayList.append(thisNode[0])
        #目标状态判定
        if thisNode[0]==endData:
            break
        #移除当前节点
        lineList.remove(thisNode)
        
#plt.xlim(xmax=28,xmin=0)
plt.ylim(ymax=7,ymin=0)
plt.xlabel('同层次节点个数')
plt.ylabel('节点层次')

#给每个节点合适的x坐标,便于显示,y坐标是其层次。
def getTheXEnd(array):
    x=[]
    times=[1,1,1,1,1,1]
    for i in array:
        for k in range(6):
            if i[1]==k+1:
                x.append(times[k]*5)
                times[k]+=1
    return x
#x,y存储节点位置,lable存储字符串化矩阵
x=getTheXEnd(reList)
y=[]
lable=[]
#得到y,将列表转化为矩阵。
for i in reList:
    y.append(7-i[1])
    strArray=str(i[0])
    lable.append(strArray[0:11]+'\n'+strArray[11:22]+'\n'+strArray[22:])
#绘制散点
plt.scatter(x,y,s=10)
#为散点添加矩阵标签

for i in range(len(x)):
    plt.annotate(lable[i],xy=(x[i],y[i]),size=lableSize)
    
plt.show()

无论是在搜索理论,还是在数据结构、python上,笔者都是新手,如果本篇文章出现谬误,恳请您在评论区指出。

你可能感兴趣的:(深度、宽度优先搜索解决九宫格问题(附python代码))