八数码问题(续)
参考:八数码问题
采用宽度搜索方法有其缺陷一面。当搜索层次超过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