You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Python/A算法-八数码问题.py

137 lines
4.8 KiB

# 目标序列end = [1,2,3,8,0,4,7,6,5]
# end = 1 2 3
# 8 0 4
# 7 6 5
import sys
class Statusobj:
def __init__(self):
# 状态序列
self.array = []
# 该状态下的深度
self.deep = 0
# 该状态的f(n) = g(n) + h(n)评估函数值
self.Fn = 0
# 该状态由上一个状态经何种操作得到,避免出现死循环
# 1:up 2:down 3:left 4:right
self.preoperate = 0
self.Father = Statusobj
# 判断八数码问题是否有解,在于若两个状态的逆序奇偶性 相同,则可相互到达,否则不可相互到达
def getreversenum(array): # 获取当前状态的逆序数和
count = 0
for i in range(len(array)):
for j in range(i+1, len(array)):
if array[j] < array[i]:
count += 1
return count
def countnotinplace(array, end): # 统计当前状态和目标状态相比较的不在位将牌数
count = 0
for i in range(len(array)):
if array[i] != end[i] and array[i] != 0:
count += 1
return count
def selectoperate(i, preoperate): # 通过当前状态下的序列下标和上一次操作符得到可选择的操作
selected = []
if 3 <= i <= 8 and preoperate != 2:
selected.append(1)
if 0 <= i <= 5 and preoperate != 1:
selected.append(2)
if i in [1, 2, 4, 5, 7, 8]:
if preoperate != 4:
selected.append(3)
if i in [0, 1, 3, 4, 6, 7]:
if preoperate != 3:
selected.append(4)
return selected
def op(i, x): # 通过操作符得到新下标
if i == 1: # up
return x-3
if i == 2: # down
return x+3
if i == 3: # left
return x-1
if i == 4: # right
return x+1
def newarray(operate, array): # 通过操作符拓展的新节点
subscript = array.index(0)
newindex = op(operate, subscript)
temp = array[subscript]
array[subscript] = array[newindex]
array[newindex] = temp
return array
def printarray(array):
print(f"{array[:3]}\n{array[3:6]}\n{array[6:]}\n", end='')
def main():
openlist = [] # open表保存待扩展实例对象
closelist = [] # close表
endarray = [1, 2, 3, 8, 0, 4, 7, 6, 5]
initobj = Statusobj()
initobj.array = list(map(int, input("请输入原始状态的八数码序列:").split()))
initobj.deep = 0 # 初始化执行深度
initobj.Fn = initobj.deep + countnotinplace(initobj.array, endarray)
openlist.append(initobj)
array_reverse = getreversenum(initobj.array) - initobj.array.index(0)
end_reverse = getreversenum(endarray) - endarray.index(0)
if array_reverse % 2 == end_reverse % 2:
while True:
if openlist[0].array == endarray: # 达到目标状态
end_list = []
end_list.append(openlist[0])
deep = openlist[0].deep
closelist.append(openlist[0])
del openlist[0]
father = closelist[-1].Father
while father.deep >= 1:
end_list.append(father)
father = father.Father
end_list.append(initobj)
print("目标状态:")
printarray(endarray)
print("【变换成功,共需要" + str(deep) + "次变换】")
d = 0
for it in reversed(end_list):
print("当前深度为:{}".format(d))
d += 1
print("操作序号为:{}".format(it.preoperate))
printarray(it.array)
sys.exit()
else:
zero_index = openlist[0].array.index(0)
temp_list = []
operation = selectoperate(zero_index, openlist[0].preoperate)
for ope in operation:
copy_array = openlist[0].array.copy() # .copy()复制序列,必不可少
new_array = newarray(ope, copy_array)
new_obj = Statusobj()
new_obj.array = new_array # 序列
new_obj.deep = openlist[0].deep + 1 # 深度
new_obj.preoperate = ope # 上一操作符
new_obj.Father = openlist[0] # 父亲节点
new_obj.Fn = new_obj.deep + countnotinplace(new_array, endarray) # 评估函数
temp_list.append(new_obj)
closelist.append(openlist[0])
del openlist[0]
for item in temp_list:
openlist.append(item)
openlist.sort(key=lambda x: x.Fn)
else:
print("此情况下无解!")
main()
# 2 8 3 1 6 4 7 0 5