1.问题描述
八数码难题是在一个3×3的棋盘上随机分布着八位数码(1-8),与空白格相连的上下左右的数码可以移动到空白格中,
通过移动数码,使得棋盘转化为目标状态,如下图所示:
2.问题分析
因为只有紧靠空白格的数码才能移动,因此仅为空白格制定上、下、左、右4种走步,空白格移动的唯一约束是不能移出棋盘。
为了便于编程实现,用数码0来代替空白格,这样问题就转换成了通过交换数码0和它上下左右的数码位置,最终达到目标状态
的问题。
3.算法设计
程序实现过程中用到的数据结构有:
(1)八数码的状态类State
每个节点的状态包括节点状态state、节点的父节点parent和节点中空格的可移动方向direction。其中初始节点没有父节点。
(2)openTable用来存放待扩展的节点。
算法执行的步骤:
(1)设置初始状态initial_state对象,并从该对象开始进行宽度优先搜索;
(2)把初始节点放入openTable中。
(3)如果open表非空,做循环
①从open表中最前端取出第一个节点node
②如果node.state和target_state相等,则查找并记录最优求解路径,并返回最优路径path、扩展节点数n和生成节点数generate_Numbers;
③否则调用generateSubStates方法获取当前节点node的所有子节点,并将其放入openTable的末尾。返回①继续执行。
4.填空完善程序。
import numpy as np
import copy
class State:
symbol = 0
def __init__(self, state, directionFlag=None, parent=None):
self.state = state #3*3的数组存放八数码的状态
self.direction = ['up', 'down', 'right', 'left'] #移动的方向
if directionFlag:
self.direction.remove(directionFlag) #删除节点返回父节点的移动方向
self.parent = parent #记录节点的父节点
def showInfo(self,flag): #打印输出八数码的状态
for i in range(3):
for j in range(3):
print(self.state[i, j], end=' ')
print()
if flag:print(' ↓')
return
def getEmptyPos(self): #查找0即空格的位置
postion = np.where(self.state == self.【1】)
return postion
def generateSubStates(self):#生成子节点
if not self.direction: #如果空格没有可以移动的方向
return []
subStates = [] #存放当前节点所有子节点的列表
row, col = self.getEmptyPos() #记录0即空格的位置
if 'left' in self.direction and col > 0: #当前状态允许向左移动,且其列号大于0
s = copy.deepcopy(self.state) #深拷贝
#标志位symbol=0向左移动,产生新的状态节点,加入到subStates中
s[row, col],s[row, col-1]=s[row, col-1],s[row, col] #交换0和其左侧数字的位置
new_State = State(s, directionFlag='right', parent=self) #创建一个新节点,且其不可以向右移动,并记录其父节点
subStates.append(new_State)
if 'right' in self.direction and col<2: #当前状态允许向右移动,且其列号小于2
s = copy.deepcopy(self.state)
# 标志位symbol=0向右移动,产生新的状态节点,加入到subStates中
s[row, col],s[row, col+1]=s[row, col+1],s[row, col]
new_State = State(s, directionFlag='left', parent=self)
subStates.append(【2】)
if 'up' in self.direction and row > 0: #当前状态允许向上移动,且其行号大于0
s = copy.deepcopy(self.state)
# 标志位symbol=0向上移动,产生新的状态节点,加入到subStates中
s[row, col],s[row-1, col]=s[row-1, col],s[row, col]
new_State = State(s, directionFlag='down', parent=self)
subStates.append(new_State)
if 'down' in self.direction and 【3】: #当前状态允许向下移动,且其行号小于2
s = copy.deepcopy(self.state)
# 标志位symbol=0向下移动,产生新的状态节点,加入到subStates中
s[row, col],s[row+1, col]=s[row+1, col],s[row, col]
new_State = State(s, directionFlag='up', parent=self)
subStates.append(new_State)
return 【4】 #返回所有生成的子节点
def BFS(self):
openTable = [] #利用open表存放待扩展的节点
openTable.append(self)#将初始状态放入Open表
n = 0 #记录已扩展节点的个数
generate_Numbers=1
while len(【5】) > 0: #open表不为空
node = openTable.pop(0) #获取open表中的第一个节点
if np.array_equal(node.state,target_state): #节点是目标节点
path=[] #记录解路径,从目标节点往回找
while node.parent: #当前节点存在父节点
path.append(node.parent) #将父节点存入path列表中
node = node.parent
path.reverse() #路径反向,即转为从初始节点到目标节点的解路径
return 【6】, n, generate_Numbers #返回解路径、扩展的节点个数n和生成的节点个数generate_Numbers
else: #当前节点不是目标节点
subStates = node.generateSubStates() #获取当前节点的子节点
generate_Numbers+=len(subStates)
if len(subStates)>0: #当前节点存在子节点
openTable.extend(subStates) #将子节点存入open表的末尾
n += 1 #已扩展节点的个数加1
else:
return None, None, None #无解
target_state = np.array([[7, 5, 8], [2, 4, 1], [3, 6, 0]])
initial_state = State(np.array([[0, 7, 8], [2, 5, 4], [3, 6, 1]]))
path,n1,n2 = initial_state.BFS()
if path: #找到解路径
for node in path: #打印解路径
node.【7】(1) #调用方法打印最优路径上的状态
target=State(target_state) #单独打印目标节点
target.showInfo(0)
print("扩展节点数:%d" % n1)
print("生成节点数:%d" % n2)
第1空
第2空
第3空
第4空
第5空
第6空
第7空
最新发布