为了能打印出能移动到目标的移动步骤,一个自然的想法是随时管理一个到达当前节点的最短移动路径,这个想法的实现太慢了。还是选择常用的方法,建立搜索树。这个搜索树是一个字典,用来记录节点之间父子关系,字典名为 son_father。
原来展开节点后要放入open,现在还要维护son_father。向son_father 压入now 时,压入的是:now:now的父,这样的键对。以后到达目标节点,可以在搜索树son_fathe中反溯,从目标找父,在找父,一直找到startlayout。形成移动路径。
from collections import deque
closed = [] # 字典
open = deque([])
son_father = {} # 记录搜索树。
# 交换规则
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]}
# 与不显示路径的代码相比,需修改exapnd维护son_father,需定义个生成路径的find_path 函数。
def swap_chr(str, i, j): #交换字串str的i,j位置的字符
str2list = list(str)
str2list[i], str2list[j] = str2list[j],str2list[i]
a = ''.join(str2list) # 转回字串
return a
# 现在重新定义展开函数 expand,再加上一个工作维护son_father
# 现在expan 直接操作全局变量,没有返回值。(注意没有返回值的函数是不能用来赋值的)
def expand(now): # 展开now,这里做两个事,管理open 和 son_father
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中
open.append(newstr)
if son_father.get(newstr) == None:# 在son_father中查找键值newstr出现过吗,如无,加入键值:newstr:now
son_father[newstr] = now
return
# 在son_father搜索一条到goal 移动路径
def find_path(son_father,goal):
if son_father.get(goal) == None: # goal 没父
print('别瞎搜,找不到这样的路径')
return
list_of_step = deque([])
current = goal
list_of_step.appendleft(current) # 此处有误,没有把current放进去。
while son_father[current] != -1: # endlayout有父。
current = son_father[current]
list_of_step.appendleft(current) # 从前面(左侧)放入
return list_of_step # 返回搜索树中从根节点到goal的路径
# 主代码
if __name__ == "__main__":
startlayout = "541203786"
endlayout = "123804765"
son_father[startlayout] = -1 # 在son_father中插入字典,键为 startlayout,值为-1, -1 表示没有父节点。
open.append(startlayout)
iter = 0 # 记录找了多少次的变量
# 以上是为下面循环准备的初始化,进入循环前必须初始化。
while 1:
iter +=1
if len(open) == 0:
print('搜索到头了,没有移动方案能移动到目标')
break
else:
# now = open.popleft() # 左弹出,队列,宽度优先
now = open.pop() # 右弹出,栈,深度优先
closed.append(now) # 放入closed
if now == endlayout: # 如到达目标
print('能移动到目标,总搜索步数:', iter)
path = find_path(son_father,endlayout) # 生成移动路径
print('需要移动 {} 步,\n移动路径是:{}'.format(len(path),path))
break
else:
expand(now) # 展开当前now 节点,管理open和son_father
运行结果:
能移动到目标,总搜索步数: 14104
需要移动 129 步,
移动路径是:deque(['541203786', '541230786', '540231786', '504231786', '534201786', '534210786', '530214786', '503214786', '513204786', '513284706', '513284760', '513280764', '513208764', '513268704', '513268740', '513260748', '513206748', '513026748', '013526748', '103526748', '123506748', '123560748', '120563748', '102563748', '162503748', '162530748', '160532748', '106532748', '136502748', '136542708', '136542780', '136540782', '136504782', '136584702', '136584720', '136580724', '136508724', '136528704', '136528074', '136028574', '136208574', '106238574', '016238574', '216038574', '216308574', '206318574', '260318574', '268310574', '268314570', '268314507', '268314057', '268014357', '268104357', '208164357', '028164357', '128064357', '128604357', '108624357', '180624357', '184620357', '184627350', '184627305', '184627035', '184027635', '184207635', '104287635', '014287635', '214087635', '214807635', '204817635', '240817635', '247810635', '247815630', '247815603', '247815063', '247015863', '247105863', '207145863', '027145863', '127045863', '127405863', '107425863', '170425863', '175420863', '175423860', '175423806', '175423086', '175023486', '175203486', '175230486', '170235486', '107235486', '137205486', '137250486', '130257486', '103257486', '153207486', '153027486', '153427086', '153427806', '153407826', '153047826', '153847026', '153847206', '153847260', '153840267', '153804267', '153864207', '153864270', '153860274', '153806274', '103856274', '013856274', '813056274', '813256074', '813256704', '813206754', '813026754', '013826754', '103826754', '123806754', '123856704', '123856740', '123850746', '123805746', '123845706', '123845760', '123840765', '123804765'])
把这个python文件命名为:cha2_8数码_带路径.py 保存。