八数码问题——盲目搜索(带移动步骤)

该代码实现了一个基于搜索树的算法,用于找到从给定初始布局到目标布局的最短移动路径。通过维护一个字典`son_father`记录节点间的父子关系,当到达目标节点时,可以反溯路径。程序采用深度优先策略,最终找到了一个129步的移动方案。

为了能打印出能移动到目标的移动步骤,一个自然的想法是随时管理一个到达当前节点的最短移动路径,这个想法的实现太慢了。还是选择常用的方法,建立搜索树。这个搜索树是一个字典,用来记录节点之间父子关系,字典名为 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 保存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值