2020-09-16

95 篇文章 2 订阅

八数码问题

这是迄今为止,除数独外最吸引我的问题。采用宽度搜索方法。
问题描述:
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某个数字。棋盘中留有一个空格,空格用0表示。空格周围的棋子可以移动到空格中。给出一种初始布局(初始状态)和目标布局,找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
编程思路:
1、建立保存链表结构的类,包括layer(层次)、father(父亲)和sun(儿子)。
2、建立基础数据表,确定空格每一种状态(位置)能够移动(变化)到下一个状态(位置)的信息表,移动的方式有四种,上、下、左、右移动一格(0与该数字互换),不能移出边界。
3、建立一个函数,根据一个状态列表生成下一个状态(多态)的多个列表。
4、为防止状态逆向变化,进行去重处理,确保状态无重复。
5、找到目标状态后,通过逆向搜根,获取各中间状态列表。

start = [7, 2, 3, 1, 6, 4, 8, 0, 5]
# start = [2, 8, 3, 1, 6, 4, 7, 0, 5]
end = [1, 2, 3, 8, 0, 4, 7, 6, 5]
# 记录空格(0)从一个状态(位置)变化到下一个状态(位置)信息
base = [[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]]]
sum_list = []   # 存放链表数据
print("start", start)
print("end", end)


class Lists:  # 建立一个链表类,按层次保存状态列表
    def __init__(self, layer, father):
        self.layer = layer
        self.sun = moving(father)
        self.father = father


def moving(state):  # 统计从一个状态(father)变化到下一个状态(sun)的所有列表
    sun = []
    zero_ind = state.index(0)
    for b in base[zero_ind][1]:
        te = copy.deepcopy(state)
        te[b], te[zero_ind] = 0, te[b]
        sun.append(te)
    return sun


if __name__ == '__main__':
    import time
    import copy

    t0 = time.time()
    s = [start]
    c = 1
    result = []
    while True:
        for f in s:
            L = Lists(c, f)    # 实例化链表类
            for g in L.sun:
                if g == end:
                    print("成功!共搜索{}层。".format(c))
                    sum_list.append(L)
                    result.append(end)
                    for i in range(c, 0, -1):   # 逆向搜根
                        for pp in sum_list:
                            if pp.layer == i and (end in pp.sun):
                                result.append(pp.father)
                                end = pp.father
                                break
                    result.reverse()
                    for r in result:
                        print(r)
                    print("耗时{}s。".format(time.time()-t0))
                    exit()
                for a in sum_list:    # 去重,关键环节
                    if a.layer == c-1 and a.father in L.sun:
                        L.sun.remove(a.father)
            sum_list.append(L)
        s = []
        for i in sum_list:
            if i.layer == c:
                for ii in i.sun:
                    s.append(ii)
        c += 1
        if c > 20:
            print("搜索层次超过设定值,失败!")
            exit()
D:\Python\fang\venv\Scripts\python.exe D:/Python/study/20200908/test6.py
start [7, 2, 3, 1, 6, 4, 8, 0, 5]
end [1, 2, 3, 8, 0, 4, 7, 6, 5]
成功!共搜索15层。
[7, 2, 3, 1, 6, 4, 8, 0, 5]
[7, 2, 3, 1, 6, 4, 0, 8, 5]
[7, 2, 3, 0, 6, 4, 1, 8, 5]
[0, 2, 3, 7, 6, 4, 1, 8, 5]
[2, 0, 3, 7, 6, 4, 1, 8, 5]
[2, 6, 3, 7, 0, 4, 1, 8, 5]
[2, 6, 3, 0, 7, 4, 1, 8, 5]
[2, 6, 3, 1, 7, 4, 0, 8, 5]
[2, 6, 3, 1, 7, 4, 8, 0, 5]
[2, 6, 3, 1, 0, 4, 8, 7, 5]
[2, 0, 3, 1, 6, 4, 8, 7, 5]
[0, 2, 3, 1, 6, 4, 8, 7, 5]
[1, 2, 3, 0, 6, 4, 8, 7, 5]
[1, 2, 3, 8, 6, 4, 0, 7, 5]
[1, 2, 3, 8, 6, 4, 7, 0, 5]
[1, 2, 3, 8, 0, 4, 7, 6, 5]
耗时4.986737966537476s。

Process finished with exit code 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值