2020-09-18

95 篇文章 2 订阅

八数码问题(续)

参考:八数码问题
采用宽度搜索方法有其缺陷一面。当搜索层次超过15层后,花费的时间越来越长。如何改进?采用双向搜索的方式使得效率大大提升。
程序修改思路:
1、增加判断是否有解函数。
2、涉及到2次搜根,将该流程写成函数。
3、因为是双向搜索,把搜索过程写成一个函数,以便重复使用。

start = [7, 2, 3, 1, 6, 4, 8, 0, 5]
end = [1, 2, 3, 8, 0, 4, 7, 6, 5]
# start = [7, 2, 3, 1, 6, 4, 8, 0, 5]
# end = [8, 1, 3, 5, 0, 4, 6, 7, 2]
# 记录空格(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 = []  # 存放链表数据
sum_list1 = []
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


def solution(state):   # 判断是否有解
    count = 0
    for _i in range(9):
        if _i != 0:
            for _j in range(_i):
                if state[_j] != 0 and state[_i] > state[_j]:
                    count += 1
    return count


def search(summary, tier, state):    # 把某个层级所有状态列表汇总到一个列表
    for f in state:
        lis = Lists(tier, f)  # 实例化链表类
        for a in summary:  # 去重,关键环节
            if a.layer == tier - 1 and a.father in lis.sun:
                lis.sun.remove(a.father)
        summary.append(lis)
    ss = []
    for i in summary:
        if i.layer == tier:
            for ii in i.sun:
                ss.append(ii)
    return ss


def root(direction, layer, group):  # 搜根
    global middle
    global count
    temp = []
    m = middle
    for i1 in range(layer, 0, -1):
        for g in group:
            if g.layer == i1 and (m in g.sun):
                temp.append(g.father)
                m = g.father
    if direction == 1:   # 正方向搜索时,完成后逆序输出
        temp.reverse()
        temp.append(middle)
    for t in temp:
        print(t)
    count+=len(temp)


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

    t0 = time.time()
    c1 = solution(start)
    c2 = solution(end)
    if (c1 - c2) % 2 != 0:
        print("无解!")
        exit()
    s, e = [start], [end]
    c = 1
    count=0
    result, result1, middle = [], [], []
    while True:
        list_order = search(sum_list, c, s)      # 从开始状态向目标状态搜索
        result.append(list_order)
        list_reverse = search(sum_list1, c, e)   # 从目标状态向开始状态搜索
        result1.append(list_reverse)
        for r in result:
            for r1 in r:
                for r2 in result1:
                    for r3 in r2:
                        if r1 == r3:
                            middle = r1           # 搜到共同的中间结果
                            root(1, c, sum_list)
                            root(0, c, sum_list1)
                            print("成功!共搜索{}层。".format(count-1))
                            print("耗时{}s。".format(time.time() - t0))
                            exit()
        c += 1
        s = list_order
        e = list_reverse
D:\Python\Python38\python.exe D:/Python/study/20200908/test9.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]
耗时0.04318380355834961s。

Process finished with exit code 0

把start和end数据更换为
start [7, 2, 3, 1, 6, 4, 8, 0, 5]
end [8, 1, 3, 5, 0, 4, 6, 7, 2]
得到的搜索结果如下:

D:\Python\Python38\python.exe D:/Python/study/20200908/test9.py
start [7, 2, 3, 1, 6, 4, 8, 0, 5]
end [8, 1, 3, 5, 0, 4, 6, 7, 2]
成功!共搜索19层。
[7, 2, 3, 1, 6, 4, 8, 0, 5]
[7, 2, 3, 1, 0, 4, 8, 6, 5]
[7, 0, 3, 1, 2, 4, 8, 6, 5]
[0, 7, 3, 1, 2, 4, 8, 6, 5]
[1, 7, 3, 0, 2, 4, 8, 6, 5]
[1, 7, 3, 8, 2, 4, 0, 6, 5]
[1, 7, 3, 8, 2, 4, 6, 0, 5]
[1, 7, 3, 8, 0, 4, 6, 2, 5]
[1, 0, 3, 8, 7, 4, 6, 2, 5]
[1, 3, 0, 8, 7, 4, 6, 2, 5]
[1, 3, 4, 8, 7, 0, 6, 2, 5]
[1, 3, 4, 8, 7, 5, 6, 2, 0]
[1, 3, 4, 8, 7, 5, 6, 0, 2]
[1, 3, 4, 8, 0, 5, 6, 7, 2]
[1, 3, 4, 8, 5, 0, 6, 7, 2]
[1, 3, 0, 8, 5, 4, 6, 7, 2]
[1, 0, 3, 8, 5, 4, 6, 7, 2]
[0, 1, 3, 8, 5, 4, 6, 7, 2]
[8, 1, 3, 0, 5, 4, 6, 7, 2]
[8, 1, 3, 5, 0, 4, 6, 7, 2]
耗时0.19618821144104004s。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值