八数码难题是一个典型的搜索问题,出现在各种教材中,用来演示盲目搜索,启发式搜索的实现过程。这里给出的程序遵循标准的盲目搜索、启发式搜索算法编写,供初学基础搜索的同学比对参考。这里先给出只显示问题可解,不显示移动步骤的代码,可以直接简明地显示盲目搜索的步骤。
from collections import deque # 导入双向列表对象
'''把九宫格描述成字串,空格用0表示,移动九宫格就是交换字串字符,0与其它位置的字符交换'''
# 声明全局变量
closed = []
open = deque([])
# 交换规则,不能随意交换字符。对字串'123405678',0在位置4,即在九宫格中间,可以和九宫格的四个相邻位置交换,就是和字串的四个位置1,3,5,7。以字典形式给出交换规则,key是0的位置,value是可允许的交换位置。
dic_of_rule = {0:[1, 3], 1:[0, 2, 4], 2:[1, 5],
3:[0,4,6], 4:[1,3,5,7], 5:[2,4,8],
6:[3,7], 7:[4,6,8], 8:[5,7]}
# 定义函数
def swap_chr(str1, i, j): #交换字串str的i,j位置的字符,把字串转成list交换,然后变回字串
str2list = list(str1)
str2list[i], str2list[j] = str2list[j],str2list[i]
a = ''.join(str2list) # 转回字串
return a
# 盲目搜索的三个步骤:open弹节点n并放closed里面,与目标比较(成功退出),展开 n 的子节点放入open。
# 这里把展开一个节点做出一个函数,返回值是一个展出的串的列表。
def expand(now): # 展开now
expanded = []
zero_pos = now.index("0")
allowed_pos = dic_of_rule[zero_pos]#当前可进行交换的位置集合
for k in allowed_pos:
newstr = swap_chr(now,k, zero_pos) # 与可允许的交换位置执行交换,生成newstr。
if newstr not in closed: # 检测newstr在不在closed中
expanded.append(newstr)
return expanded
# 主代码
if __name__ == "__main__":
startlayout = "541203786"
endlayout = "123804765"
open.append(startlayout)
iter = 0 # 记录找了多少次的变量
# 以上是为下面循环准备的初始化,进入循环前必须初始化。
while 1:
iter +=1
if len(open) == 0:
print('不能移动到目标。')
break
else:
# now = open.popleft() # 从open 右弹出,队列,宽度优先搜索
now = open.pop() # 从open 左弹出,栈,深度优先搜索
closed.append(now) # 放入closed
if now == endlayout: # 比较检查找没找到目标,如到达目标
print('共搜索{}步,能移动到目标'.format(iter))
break
else:
expanded = expand(now) # 如没有找到,展开当前now 节点。
open.extend(expanded) # 把展开节点加到open中,这里用extend方法实现
# 此处记住,open=open.extend(expanded)会出现错误,因为extend()方法是in-place的,没有返回值。
运行结果:
共搜索14104步,能移动到目标