我用Python开发了自己的程序来解决8-puzzle。最初,我使用“盲”或不知情的搜索(基本上是暴力破解)生成和探索所有可能的后继者并使用广度优先搜索。当它找到“目标”状态时,它基本上回溯到初始状态并提供(我相信)是解决它的最优化步骤。当然,最初的状态是搜索需要花费大量时间并在找到目标之前产生超过100,000个状态。
然后我添加了启发式 - 曼哈顿距离。解决方案开始呈指数级增长,并且探索状态较少。 但我的困惑是,有些时候,生成的优化序列比使用盲目或不知情的搜索所达到的更长。
我正在做的基本上是这样的:
我不确定这是否符合贪婪优先或A *。
我的问题是,这是曼哈顿距离启发式的一个固有缺陷,有时它无法提供最佳解决方案,或者我做错了什么。
以下是代码。我很抱歉这不是一个非常干净的代码,但大多是顺序的,它应该很容易理解。我也为长代码道歉 - 我知道我需要优化它。也欢迎任何清理代码的建议/指导。这就是它:
import numpy as np
from copy import deepcopy
import sys
# calculate Manhattan distance for each digit as per goal
def mhd(s, g):
m = abs(s // 3 - g // 3) + abs(s % 3 - g % 3)
return sum(m[1:])
# assign each digit the coordinate to calculate Manhattan distance
def coor(s):
c = np.array(range(9))
for x, y in enumerate(s):
c[y] = x
return c
#################################################
def main():
goal = np.array( [1, 2, 3, 4, 5, 6, 7, 8, 0] )
rel = np.array([-1])
mov = np.array([' '])
string = '102468735'
inf = 'B'
pos = 0
yes = 0
goalc = coor(goal)
puzzle = np.array([int(k) for k in string]).reshape(1, 9)
rnk = np.array([mhd(coor(puzzle[0]), goalc)])
while True:
loc = np.where(puzzle[pos] == 0) # locate '0' (blank) on the board
loc = int(loc[0])
child = np.array([], int).reshape(-1, 9)
cmove = []
crank = []
# generate successors on possible moves - new states no repeats
if loc > 2: # if 'up' move is possible
succ = deepcopy(puzzle[pos])
succ[loc], succ[loc - 3] = succ[loc - 3], succ[loc]
if ~(np.all(puzzle == succ, 1)).any(): # repeat state?
child = np.append(child, [succ], 0)
cmove.append('up')
crank.append(mhd(coor(succ), goalc)) # manhattan distance
if loc < 6: # if 'down' move is possible
succ = deepcopy(puzzle[pos])
succ[loc], succ[loc + 3] = succ[loc + 3], succ[loc]
if ~(np.all(puzzle == succ, 1)).any(): # repeat state?
child = np.append(child, [succ], 0)
cmove.append('down')
crank.append(mhd(coor(succ), goalc))
if loc % 3 != 0: # if 'left' move is possible
succ = deepcopy(puzzle[pos])
succ[loc], succ[loc - 1] = succ[loc - 1], succ[loc]
if ~(np.all(puzzle == succ, 1)).any(): # repeat state?
child = np.append(child, [succ], 0)
cmove.append('left')
crank.append(mhd(coor(succ), goalc))
if loc % 3 != 2: # if 'right' move is possible
succ = deepcopy(puzzle[pos])
succ[loc], succ[loc + 1] = succ[loc + 1], succ[loc]
if ~(np.all(puzzle == succ, 1)).any(): # repeat state?
child = np.append(child, [succ], 0)
cmove.append('right')
crank.append(mhd(coor(succ), goalc))
for s in range(len(child)):
if (inf in 'Ii' and crank[s] == min(crank)) \
or (inf in 'Bb'):
puzzle = np.append(puzzle, [child[s]], 0)
rel = np.append(rel, pos)
mov = np.append(mov, cmove[s])
rnk = np.append(rnk, crank[s])
if np.array_equal(child[s], goal):
print()
print('Goal achieved!. Successors generated:', len(puzzle) - 1)
yes = 1
break
if yes == 1:
break
pos += 1
# generate optimized steps by back-tracking the steps to the initial state
optimal = np.array([], int).reshape(-1, 9)
last = len(puzzle) - 1
optmov = []
rank = []
while last != -1:
optimal = np.insert(optimal, 0, puzzle[last], 0)
optmov.insert(0, mov[last])
rank.insert(0, rnk[last])
last = int(rel[last])
# show optimized steps
optimal = optimal.reshape(-1, 3, 3)
print('Total optimized steps:', len(optimal) - 1)
print()
for s in range(len(optimal)):
print('Move:', optmov[s])
print(optimal[s])
print('Manhattan Distance:', rank[s])
print()
print()
################################################################
# Main Program
if __name__ == '__main__':
main()
以下是一些初始状态和计算出的优化步骤(如果您想检查)(上面的代码会给出此选项,以便在盲目与知情搜索之间进行选择)
初始状态
- 283164507盲人:19曼哈顿:21
- 243780615盲人:15曼哈顿:21
- 102468735盲人:11曼哈顿:17
- 481520763盲人:13曼哈顿:23
- 723156480盲人:16曼哈顿:20
我故意选择了结果很快的例子(几秒钟或几分钟内)。
非常感谢您的帮助和指导。
编辑:我做了一些快速更改并设法减少了大约30多行。不幸的是,此时不能做太多 注意:我已经硬编码了初始状态和盲目与知情选择。请更改初始状态变量“string”的值和Informed / Blind变量“inf”[I / B]。谢谢!