八数码问题及代码
序
首先来看八数码问题的计算流程:
简单来说,八数码问题就是给出八数码的初始状态以及目标状态,通过空格的上下左右变换来改变八数码的状态直至找到目标状态为止。
如果没有目标的对叶子节点进行扩展,那么就是盲目搜索;如果通过一个函数值来确定拓展的叶子结点,那么就是启发式搜索。
这里给出两个估价函数。
(1)深度+错放棋子个数
(2)深度+错放棋子与目标位置的距离之和
能够手动写出上图的过程后,再看下述内容。
大致的代码思路(个人想法,可有差异)
创建三个表:open表、close表、value表
open表存放未扩展的八数码状态,close表存放已经扩展的八数码状态,value表存放未扩展的节点估价函数值。其中open表和value表八数码状态一一对应。
找到目标八数码:
(1)首先,将初始状态八数码放入open表中,其估价函数值放入value表中,close表为空。
(2)比较value表中哪个值最小,获得其在value表的位置(open表中要扩展的节点位置便知道了)。对open表中对应状态八数码进行扩展,将扩展后的状态加入到open表、估价函数值加入value表。同时将已经扩展的八数码状态移入close表中,其估价函数值直接从value表剔除。
(3)重复(2)的过程,直至从open表中找到目标状态的八数码。
上述过程忽略了无解的情况,这里解释。根据已知的结论,八数码两个状态分别排成一行,再去除0后的逆序数(线性代数相关知识)如果同奇同偶,那么这两个八数码便可以互相抵达。这样我们在最后写一个函数就可以解决了。
代码如下:
def judge(data_1,data_2):
'''判断两个八数码是否可互相到达(逆序奇偶性)'''
temp1 = copy.deepcopy(data_1[0:3])
temp2 = copy.deepcopy(data_2[0:3])
list1 = sum(temp1, [])
list2 = sum(temp2,[])
list1.remove(0)
list2.remove(0)
if getReverseOrderNumber(list1)%2==getReverseOrderNumber(list2)%2:
return True
else:
print("无解!")
return False
回溯输出寻找途径:
找到目标八数码后,遍历close表找到上一层对应的状态(深度相差1并且棋子错放个数相差1的八数码状态),通过递归的方法递归回初始状态。这是输出寻找到目标状态途径的大致思路。
代码如下:
def flashBack(data,temp_closeTable):
'''从初始八数码回溯至目标八数码'''
print(data)
tempTable=[]
'''将上一层的八数码移入tempTable'''
for item in temp_closeTable:
if item[3]==data[3]-1:
tempTable.append(item)
for item in tempTable:
if abs(errorNum(item,data))==1:
break
if item[3]==0:
print(item)
return
else:
flashBack(item,temp_closeTable)
具体代码
import numpy as np
import copy
import time
def standardEight():
'''标准的八数码'''
data = [[3,0,6],[1,8,5],[4,7,2]]
return data
def creatEight():
''''构建并返回八数码'''
data = [[],[],[],0,0]#存八数码,第一个0表示深度,第二个0表示当前状态(0:初始状态;1:0上移得来;2:0下移得来;3:0左移得来;4:0右移得来)
i = 0
while(i<9):
j = eval(input('输入第'+str(i+1)+'个数据:'))
if 0<=j<=8:
data[int(i/3)].append(j)
i = i+1
else:
print('输入数据错误!\n')
return data
def findLocation(data,findNum):
data_array=np.array(data[:3])
return np.argwhere(data_array==findNum)[0]
def errorNum(data_1,data_2):
errorNum = 0
loc_1 = findLocation(data_1,0)
loc_2 = findLocation(data_2,0)
for i in range(3):
for j in range(3):
if data_1[i][j] != data_2[i][j]:
errorNum = errorNum + 1
if loc_1[0]==loc_2[0 and loc_1[1]==loc_2[1]]:
return errorNum
else:
return errorNum-1
def getReverseOrderNumber(list):
'''获得逆序数'''
num = 0
for i in range(1,len(list)):
for j in range(i):
if list[j]>list[i]:
num = num+1
return num
def judge(data_1,data_2):
'''判断两个八数码是否可互相到达(逆序奇偶性)'''
temp1 = copy.deepcopy(data_1[0:3])
temp2 = copy.deepcopy(data_2[0:3])
list1 = sum(temp1, [])
list2 = sum(temp2,[])
list1.remove(0)
list2.remove(0)
if getReverseOrderNumber(list1)%2==getReverseOrderNumber(list2)%2:
return True
else:
print("无解!")
return False
'''构建估价函数'''
def aStarOne(data_1,data_2):
'''八数码错放棋子个数的估价函数'''
'''data_1:标准八数码;data_2:构造的八数码'''
return errorNum(data_1,data_2)+data_2[3]
'''errorNum = 0
for i in range(3):
for j in range(3):
if data_1[i][j] != data_2[i][j]:
errorNum = errorNum+1
return errorNum+data_2[3]-1'''
def aStarTwo(data_1,data_2):
'''错放棋子与目标位置距离之和构成的估价函数'''
errorDistance = 0
for i in range(1,9):
loc_1 = findLocation(data_1,i)
loc_2 = findLocation(data_2,i)
errorDistance = abs(loc_1[0]+loc_1[1]-loc_2[0]-loc_2[1])+errorDistance#求曼哈顿距离
return errorDistance+data_2[3]
'''构建移动函数'''
def up(data):
'''上移'''
zeroLoc = findLocation(data,0)
i = int(zeroLoc[0])
j = int(zeroLoc[1])
if i>0:
temp = data[i][j]
data[i][j] = data[i-1][j]
data[i-1][j] = temp
data[3] = data[3] + 1
data[4] = 1
return data
else:
return
def down(data):
'''下移'''
zeroLoc = findLocation(data, 0)
i = int(zeroLoc[0])
j = int(zeroLoc[1])
if i<2:
temp = data[i][j]
data[i][j] = data[i+1][j]
data[i+1][j] = temp
data[3] = data[3] + 1
data[4] = 2
return data
else:
return
def left(data):
'''左移'''
zeroLoc = findLocation(data, 0)
i = int(zeroLoc[0])
j = int(zeroLoc[1])
if j>0:
temp = data[i][j]
data[i][j] = data[i][j-1]
data[i][j-1] = temp
data[3] = data[3] + 1
data[4] = 3
return data
else:
return
def right(data):
'''右移'''
zeroLoc = findLocation(data, 0)
i = int(zeroLoc[0])
j = int(zeroLoc[1])
if j<2:
temp = data[i][j]
data[i][j] = data[i][j+1]
data[i][j+1] = temp
data[3] = data[3] + 1
data[4] = 4
return data
else:
return
def overCondition(data,openTable):
'''判断目标八数码是否在openTable中,
返回 True or False
并返回目标八数码在openTable的索引
'''
index = -1
for item in openTable:
index = index + 1
j = 0
for i in range(3):
if item[i] == data[i]:
j = j + 1
if j == 3:
print("搜索成功!")
return True,index
return False,-1
def extend(data_1,data_2,openTable,valueTable,closeTable,mode,index2):
'''扩展八数码,并将被扩展的八数码移除出openTable'''
'''data_1:被扩展的八数码;data_2:标准八数码'''
if data_1[4]==0:
data1=up(copy.deepcopy(data_1))
if data1:
openTable.append(data1)
if mode is "1":
valueTable.append(aStarOne(data_2,data1))
else:
valueTable.append(aStarTwo(data_2, data1))
data2=down(copy.deepcopy(data_1))
if data2:
openTable.append(data2)
if mode is "1":
valueTable.append(aStarOne(data_2, data2))
else:
valueTable.append(aStarTwo(data_2, data2))
data3=left(copy.deepcopy(data_1))
if data3:
openTable.append(data3)
if mode is "1":
valueTable.append(aStarOne(data_2, data3))
else:
valueTable.append(aStarTwo(data_2, data3))
data4=right(copy.deepcopy(data_1))
if data4:
openTable.append(data4)
if mode is "1":
valueTable.append(aStarOne(data_2, data4))
else:
valueTable.append(aStarTwo(data_2, data4))
del valueTable[index2]
openTable.remove(data_1)
closeTable.append(data_1)
elif data_1[4]==1:
data1 = up(copy.deepcopy(data_1))
if data1:
openTable.append(data1)
if mode is "1":
valueTable.append(aStarOne(data_2, data1))
else:
valueTable.append(aStarTwo(data_2, data1))
data3 = left(copy.deepcopy(data_1))
if data3:
openTable.append(data3)
if mode is "1":
valueTable.append(aStarOne(data_2, data3))
else:
valueTable.append(aStarTwo(data_2, data3))
data4 = right(copy.deepcopy(data_1))
if data4:
openTable.append(data4)
if mode is "1":
valueTable.append(aStarOne(data_2, data4))
else:
valueTable.append(aStarTwo(data_2, data4))
del valueTable[index2]
openTable.remove(data_1)
closeTable.append(data_1)
elif data_1[4]==2:
data2 = down(copy.deepcopy(data_1))
if data2:
openTable.append(data2)
if mode is "1":
valueTable.append(aStarOne(data_2, data2))
else:
valueTable.append(aStarTwo(data_2, data2))
data3 = left(copy.deepcopy(data_1))
if data3:
openTable.append(data3)
if mode is "1":
valueTable.append(aStarOne(data_2, data3))
else:
valueTable.append(aStarTwo(data_2, data3))
data4 = right(copy.deepcopy(data_1))
if data4:
openTable.append(data4)
if mode is "1":
valueTable.append(aStarOne(data_2, data4))
else:
valueTable.append(aStarTwo(data_2, data4))
del valueTable[index2]
openTable.remove(data_1)
closeTable.append(data_1)
elif data_1[4]==3:
data1 = up(copy.deepcopy(data_1))
if data1:
openTable.append(data1)
if mode is "1":
valueTable.append(aStarOne(data_2, data1))
else:
valueTable.append(aStarTwo(data_2, data1))
data2 = down(copy.deepcopy(data_1))
if data2:
openTable.append(data2)
if mode is "1":
valueTable.append(aStarOne(data_2, data2))
else:
valueTable.append(aStarTwo(data_2, data2))
data3 = left(copy.deepcopy(data_1))
if data3:
openTable.append(data3)
if mode is "1":
valueTable.append(aStarOne(data_2, data3))
else:
valueTable.append(aStarTwo(data_2, data3))
del valueTable[index2]
openTable.remove(data_1)
closeTable.append(data_1)
elif data_1[4]==4:
data1 = up(copy.deepcopy(data_1))
if data1:
openTable.append(data1)
if mode is "1":
valueTable.append(aStarOne(data_2, data1))
else:
valueTable.append(aStarTwo(data_2, data1))
data2 = down(copy.deepcopy(data_1))
if data2:
openTable.append(data2)
if mode is "1":
valueTable.append(aStarOne(data_2, data2))
else:
valueTable.append(aStarTwo(data_2, data2))
data4 = right(copy.deepcopy(data_1))
if data4:
openTable.append(data4)
if mode is "1":
valueTable.append(aStarOne(data_2, data4))
else:
valueTable.append(aStarTwo(data_2, data4))
del valueTable[index2]
openTable.remove(data_1)
closeTable.append(data_1)
def flashBack(data,temp_closeTable):
'''从初始八数码回溯至目标八数码'''
print(data)
tempTable=[]
'''将上一层的八数码移入tempTable'''
for item in temp_closeTable:
if item[3]==data[3]-1:
tempTable.append(item)
for item in tempTable:
if abs(errorNum(item,data))==1:
break
if item[3]==0:
print(item)
return
else:
flashBack(item,temp_closeTable)
'''运行测试'''
#a=findLocation(standEight,0)
print("输入目标八数码:")
standEight = creatEight()
print("输入初始八数码")
testEight = creatEight()
print("\n")
start_time = time.perf_counter()
if judge(standEight,testEight):
mode = input("请选择估价函数(1 or 2):")
if mode not in ["1","2"]:
print("模式选择错误,没有选择正确的估价函数!")
else:
openTable=[testEight]
closeTable=[]
valueTable=[aStarOne(standEight,testEight)]
while 1:
flag,index1 = overCondition(standEight,openTable)
if flag:
print("搜索路径如下:")
temp_closeTable = copy.deepcopy(closeTable)
flashBack(openTable[index1],temp_closeTable)
#print(openTable[index1])
#print(openTable)
#print(closeTable)
break
minNum = min(valueTable)
temp_list=[]
for i in range(len(valueTable)):
if valueTable[i] == minNum:
temp_list.append(openTable[i])
for i in range(len(temp_list)):
index2 = openTable.index(temp_list[i])
extend(temp_list[i], standEight, openTable, valueTable,closeTable,mode,index2)
end_time=time.perf_counter()
print("扩展的节点个数{}".format(len(openTable)+len(closeTable)))
print("运行时间:{}".format(end_time-start_time))