八数码问题——持续更新中(一)
最近一段时间一直在思考,没有时间写东西……
现在能够短暂坐下来,还是把前面的程序改改。
八数码问题已经写了多篇,但仍然感到意犹未尽。
其实双向搜索还是可行的。只是当时认为搜索的层数比较多,觉得这个程序的适应性比较差。但随着认识的变化,这个程序的重要性凸显出来,于是又回过头来对这个程序进行修订,暂且不提。
认识的变化还在于最多需要搜索多少层上。
有部分证据说明最多搜索不会超过30层。
基本验证思路是这样的:选定目标状态(target),然后对其他状态(original)进行遍历,筛选那些可以变化到目标状态的所有组合,计算他们(target状态与original状态)之间的曼哈顿距离,结论是最大不超过24。对这些曼哈顿距离达到24的所有组合逐一进行由始至终的层次计算。经过一段时间的运算,没有发现层次超过30的案例!虽然这个验证的过程或许有瑕疵,没有把曼哈顿距离小于24的情况进行逐一验证,但基本可以肯定,结论是正确的。
下面是完整的程序代码:
# 统计从一个状态(father)变化到下一个状态(sun)的所有列表
def moving(state):
sun = []
zero_ind = state.index(0)
for _b in base[zero_ind]:
temp = copy.deepcopy(list(state))
temp[_b], temp[zero_ind] = 0, temp[_b]
sun.append(temp)
return sun
# 建立链表类,按层次保存状态列表
class Lists:
def __init__(self, storey, father):
self.storey = storey
self.sun = moving(father)
self.father = father
# 判断是否有解
def solution(state):
_count = 0
for _i in range(9):
if state[_i] != 0:
for _j in range(_i):
if state[_j] != 0 and state[_i] > state[_j]:
_count += 1
return _count
# 代价计算,计算当前状态与目标状态间的曼哈顿距离,作为第一排序的依据,是优选种子的关键
def manhattan(state):
_count = 0
for _i in range(9):
if target[_i] != 0:
for _j in range(9):
if state[_j] == target[_i]:
_count += abs(_j % 3 - _i % 3) + abs(_j // 3 - _i // 3)
break
return _count
# 计算当前状态与目标状态的差异值,作为第二排序的依据
def diff(state):
_diff = 0
for _i in range(9):
if state[_i] != target[_i]:
_diff += 1
return _diff
if __name__ == '__main__':
import time
import copy
import itertools
t0 = time.time()
b = [0, 1, 2, 3, 4, 5, 6, 7, 8]
target = [1, 2, 3, 4, 0, 5, 6, 7, 8]
# 记录空格(0)从一个状态(位置)变化到下一个状态(位置)信息,在原来的基础上进行了精简
base = [[1, 3], [0, 2, 4], [1, 5], [0, 4, 6], [1, 3, 5, 7], [2, 4, 8], [3, 7], [4, 6, 8], [5, 7]]
all_combinations = itertools.permutations(b, 9)
number = 0 # 统计搜索层次等于或大于30的数量
count = 0 # 统计曼哈顿距离达到24的组合数量
c2 = solution(target)
for original in all_combinations:
sum_list = [] # 存放记录状态链表数据
mark = 0
c1 = solution(original)
if (c1 + c2) % 2 != 0:
continue
if manhattan(original) == 24:
count += 1
source = original
seed, layer = [source], 1
while True:
for f in seed:
L = Lists(layer, f) # 实例化链表类
for t in L.sun:
if t == target:
mark = 1
if layer >= 30:
number += 1
print(original)
print("共搜索{}层。".format(layer))
break
if mark == 1:
break
# 去重
for s in sum_list:
if s.storey == layer - 1 and s.father in L.sun:
L.sun.remove(s.father)
break
sum_list.append(L)
if mark == 1:
break
# 优选种子
seed, see = [], []
for i in sum_list:
if i.storey == layer:
seed.extend(i.sun)
# 再去重
for se in seed:
if se not in see:
see.append(se)
# 以曼哈顿距离为第一排序,距离越短,排序越靠前;以差异值作为第二排序,差异值越小,排序越靠前,排序300以后的舍去
see = sorted(see, key=lambda k: manhattan(k) - diff(k))
seed = see[:300]
layer += 1
print("曼哈顿距离达到24的组合数量为{}。".format(count))
print("共有{}种组合,其搜索层次等于或大于30。".format(number))
print("耗时{}s。".format(time.time() - t0))
D:\Python\Python38\python.exe D:/Python/study/egiht7.py
(5, 4, 6, 8, 0, 7, 3, 2, 1)
共搜索30层。
(5, 6, 7, 8, 0, 1, 2, 3, 4)
共搜索30层。
(5, 7, 6, 8, 0, 1, 3, 2, 4)
共搜索30层。
(7, 8, 4, 3, 0, 6, 5, 1, 2)
共搜索30层。
(7, 8, 6, 2, 0, 4, 3, 5, 1)
共搜索30层。
(7, 8, 6, 5, 0, 4, 3, 1, 2)
共搜索30层。
(8, 4, 6, 2, 0, 7, 3, 5, 1)
共搜索30层。
(8, 4, 6, 5, 0, 7, 3, 1, 2)
共搜索30层。
(8, 5, 4, 7, 0, 6, 3, 2, 1)
共搜索30层。
(8, 5, 6, 7, 0, 2, 3, 4, 1)
共搜索30层。
(8, 5, 6, 7, 0, 4, 2, 3, 1)
共搜索30层。
(8, 6, 7, 5, 0, 2, 3, 4, 1)
共搜索30层。
(8, 6, 7, 5, 0, 4, 2, 3, 1)
共搜索30层。
(8, 7, 4, 3, 0, 6, 5, 2, 1)
共搜索30层。
(8, 7, 6, 2, 0, 1, 3, 5, 4)
共搜索30层。
(8, 7, 6, 3, 0, 2, 5, 4, 1)
共搜索30层。
(8, 7, 6, 5, 0, 4, 3, 2, 1)
共搜索30层。
曼哈顿距离达到24的组合数量为97。
共有17种组合,其搜索层次等于或大于30。
耗时279.3544330596924s。
Process finished with exit code 0
通过上述测试,发现一个现象,在曼哈顿距离达到24的组合中,凡须要运算30层的组合,其original状态当中的“0”都是处于中间位置,并且target状态中的“0”也处于中间位置,否则无须30层就能搞定!读者可以自行验算。也就是说,在曼哈顿距离达到24的组合中,只要target或original当中的“0”有一个不在中间,运算层次就可以控制在30层以下!但不能就此肯定只有曼哈顿距离达到24的组合,才有可能须要运算到30层。有下面的案例为证:
source = [0, 4, 6, 5, 8, 7, 3, 2, 1]
target = [1, 2, 3, 4, 0, 5, 6, 7, 8]
它们的曼哈顿距离只有22,但运算层次达到了30层。
中午吃饭的时候把程序稍作修改测试了曼哈顿距离为23时,层次达到或超过29的情况,发现original中的“0”果然都不在中间,并且最大只有29层。
D:\Python\Python38\python.exe D:/Python/study/egiht7.py
(5, 0, 6, 2, 8, 7, 3, 4, 1)
共搜索29层。
(5, 0, 6, 8, 4, 7, 3, 2, 1)
共搜索29层。
(5, 0, 6, 8, 7, 1, 3, 2, 4)
共搜索29层。
(5, 0, 6, 8, 7, 4, 3, 1, 2)
共搜索29层。
(5, 4, 6, 0, 8, 7, 3, 2, 1)
共搜索29层。
(5, 4, 6, 8, 2, 7, 3, 0, 1)
共搜索29层。
(5, 4, 6, 8, 7, 0, 3, 2, 1)
共搜索29层。
(5, 7, 6, 0, 8, 1, 3, 2, 4)
共搜索29层。
(5, 7, 6, 8, 1, 0, 3, 2, 4)
共搜索29层。
(5, 7, 6, 8, 2, 0, 3, 4, 1)
共搜索29层。
(5, 7, 6, 8, 2, 1, 3, 0, 4)
共搜索29层。
(5, 7, 6, 8, 4, 0, 3, 1, 2)
共搜索29层。
(5, 7, 6, 8, 4, 2, 3, 0, 1)
共搜索29层。
(7, 0, 6, 2, 8, 4, 3, 5, 1)
共搜索29层。
(7, 0, 6, 5, 8, 4, 3, 1, 2)
共搜索29层。
(7, 4, 6, 0, 8, 2, 3, 5, 1)
共搜索29层。
(7, 8, 6, 0, 2, 4, 3, 5, 1)
共搜索29层。
(7, 8, 6, 0, 5, 1, 3, 2, 4)
共搜索29层。
(7, 8, 6, 0, 5, 4, 3, 1, 2)
共搜索29层。
(7, 8, 6, 2, 4, 0, 3, 5, 1)
共搜索29层。
(7, 8, 6, 2, 5, 4, 3, 0, 1)
共搜索29层。
(7, 8, 6, 5, 1, 4, 3, 0, 2)
共搜索29层。
(7, 8, 6, 5, 2, 0, 3, 4, 1)
共搜索29层。
(7, 8, 6, 5, 2, 1, 3, 0, 4)
共搜索29层。
(7, 8, 6, 5, 4, 0, 3, 1, 2)
共搜索29层。
(7, 8, 6, 5, 4, 2, 3, 0, 1)
共搜索29层。
(8, 0, 4, 3, 7, 6, 5, 2, 1)
共搜索29层。
(8, 0, 4, 5, 7, 6, 2, 3, 1)
共搜索29层。
(8, 0, 4, 7, 5, 6, 3, 2, 1)
共搜索29层。
(8, 0, 4, 7, 6, 2, 3, 5, 1)
共搜索29层。
(8, 0, 6, 2, 4, 7, 3, 5, 1)
共搜索29层。
(8, 0, 6, 2, 7, 1, 3, 5, 4)
共搜索29层。
(8, 0, 6, 3, 4, 7, 5, 2, 1)
共搜索29层。
(8, 0, 6, 3, 7, 2, 5, 4, 1)
共搜索29层。
(8, 0, 6, 5, 2, 7, 3, 4, 1)
共搜索29层。
(8, 0, 6, 5, 4, 7, 2, 3, 1)
共搜索29层。
(8, 0, 6, 5, 4, 7, 3, 1, 2)
共搜索29层。
(8, 0, 6, 5, 7, 4, 3, 2, 1)
共搜索29层。
(8, 0, 6, 7, 2, 4, 3, 5, 1)
共搜索29层。
(8, 0, 6, 7, 5, 1, 3, 2, 4)
共搜索29层。
(8, 0, 6, 7, 5, 2, 3, 4, 1)
共搜索29层。
(8, 0, 6, 7, 5, 4, 2, 3, 1)
共搜索29层。
(8, 0, 6, 7, 5, 4, 3, 1, 2)
共搜索29层。
(8, 0, 7, 5, 6, 2, 3, 4, 1)
共搜索29层。
(8, 0, 7, 5, 6, 4, 2, 3, 1)
共搜索29层。
(8, 4, 6, 0, 2, 7, 3, 5, 1)
共搜索29层。
(8, 4, 6, 0, 5, 7, 3, 1, 2)
共搜索29层。
(8, 4, 6, 2, 5, 7, 3, 0, 1)
共搜索29层。
(8, 4, 6, 2, 7, 0, 3, 5, 1)
共搜索29层。
(8, 4, 6, 3, 7, 0, 5, 2, 1)
共搜索29层。
(8, 4, 6, 5, 1, 7, 3, 0, 2)
共搜索29层。
(8, 4, 6, 5, 7, 0, 2, 3, 1)
共搜索29层。
(8, 4, 6, 5, 7, 0, 3, 1, 2)
共搜索29层。
(8, 4, 6, 5, 7, 2, 3, 0, 1)
共搜索29层。
(8, 4, 6, 7, 1, 0, 3, 5, 2)
共搜索29层。
(8, 4, 6, 7, 3, 2, 5, 0, 1)
共搜索29层。
(8, 4, 6, 7, 5, 0, 3, 2, 1)
共搜索29层。
(8, 5, 4, 0, 7, 6, 3, 2, 1)
共搜索29层。
(8, 5, 4, 7, 2, 6, 3, 0, 1)
共搜索29层。
(8, 5, 4, 7, 6, 0, 3, 2, 1)
共搜索29层。
(8, 5, 6, 0, 3, 7, 2, 4, 1)
共搜索29层。
(8, 5, 6, 0, 4, 7, 3, 2, 1)
共搜索29层。
(8, 5, 6, 0, 7, 1, 3, 2, 4)
共搜索29层。
(8, 5, 6, 0, 7, 2, 3, 4, 1)
共搜索29层。
(8, 5, 6, 0, 7, 4, 2, 3, 1)
共搜索29层。
(8, 5, 6, 0, 7, 4, 3, 1, 2)
共搜索29层。
(8, 5, 6, 2, 1, 7, 3, 0, 4)
共搜索29层。
(8, 5, 6, 2, 7, 4, 3, 0, 1)
共搜索29层。
(8, 5, 6, 7, 2, 0, 3, 4, 1)
共搜索29层。
(8, 5, 6, 7, 3, 4, 2, 0, 1)
共搜索29层。
(8, 5, 6, 7, 4, 0, 2, 3, 1)
共搜索29层。
(8, 5, 6, 7, 4, 2, 3, 0, 1)
共搜索29层。
(8, 5, 7, 2, 6, 0, 3, 4, 1)
共搜索29层。
(8, 6, 7, 0, 2, 4, 3, 5, 1)
共搜索29层。
(8, 6, 7, 0, 5, 2, 3, 4, 1)
共搜索29层。
(8, 6, 7, 0, 5, 4, 2, 3, 1)
共搜索29层。
(8, 6, 7, 2, 5, 4, 3, 0, 1)
共搜索29层。
(8, 6, 7, 3, 2, 4, 5, 0, 1)
共搜索29层。
(8, 6, 7, 3, 4, 0, 5, 2, 1)
共搜索29层。
(8, 6, 7, 5, 2, 0, 3, 4, 1)
共搜索29层。
(8, 6, 7, 5, 3, 4, 2, 0, 1)
共搜索29层。
(8, 6, 7, 5, 4, 0, 2, 3, 1)
共搜索29层。
(8, 6, 7, 5, 4, 2, 3, 0, 1)
共搜索29层。
(8, 7, 4, 0, 2, 6, 3, 5, 1)
共搜索29层。
(8, 7, 4, 0, 3, 6, 5, 2, 1)
共搜索29层。
(8, 7, 4, 0, 5, 6, 2, 3, 1)
共搜索29层。
(8, 7, 4, 2, 5, 6, 3, 0, 1)
共搜索29层。
(8, 7, 4, 3, 2, 6, 5, 0, 1)
共搜索29层。
(8, 7, 4, 3, 6, 0, 5, 2, 1)
共搜索29层。
(8, 7, 6, 0, 2, 1, 3, 5, 4)
共搜索29层。
(8, 7, 6, 0, 3, 2, 5, 4, 1)
共搜索29层。
(8, 7, 6, 0, 4, 2, 3, 5, 1)
共搜索29层。
(8, 7, 6, 0, 5, 4, 3, 2, 1)
共搜索29层。
(8, 7, 6, 2, 1, 0, 3, 5, 4)
共搜索29层。
(8, 7, 6, 2, 5, 0, 3, 4, 1)
共搜索29层。
(8, 7, 6, 2, 5, 1, 3, 0, 4)
共搜索29层。
(8, 7, 6, 3, 2, 0, 5, 4, 1)
共搜索29层。
(8, 7, 6, 3, 4, 2, 5, 0, 1)
共搜索29层。
(8, 7, 6, 5, 2, 4, 3, 0, 1)
共搜索29层。
(8, 7, 6, 5, 4, 0, 3, 2, 1)
共搜索29层。
曼哈顿距离达到23的组合数量为572。
共有100种组合,其搜索层次等于或大于29。
耗时1654.776507616043s。
Process finished with exit code 0
要说明二点。其一,这次曼哈顿距离计算把“0”排除在外,感觉效率更高,种选300即可。是否有例外还不清楚,如果有,可以采用双向搜索重新验证一下。其二,对种子进行了去重处理,效果不错。