使用A *进行堆栈溢出以解决8-puzzle

时间:2018-04-30 02:19:57

标签: python stack-overflow a-star 8-puzzle

此代码的目标是解决N拼图(请参阅此处:https://en.wikipedia.org/wiki/15_puzzle

该函数用于获取整数0到(N ** 2-1)的加扰列表,并返回移动列表以达到求解状态,其中移动是x和y坐标的列表(例如[2,2]表示在3x3拼图中移动右下角)。我知道只有某些电路板可以解决,我测试代码的电路板绝对是可以解决的。

我尝试实现A *搜索,但似乎有一些问题,因为对于许多电路板我正在堆栈溢出。我在下面的代码中使用的板导致堆栈溢出,但是这个板将在大约20秒后返回正确的移动序列(这似乎太长了): [7,2,8,1,5,6,0,3,4]

基本思路是从一系列移动路径开始,然后找到尚未扩展的最低成本路径(成本=当前路径长度+剩余曼哈顿距离),并扩展该路径,并继续前进直到找到解决方案。

据我所知,对于一个8-puzzle的强制执行可能就好像不比A *更可行,但如果我的代码也可以用于15-puzzle,我也会喜欢它。

从我的调试开始,看起来代码有点像预期的那样工作,我只是不确定为什么它花费这么长时间并且对于大多数电路板导致堆栈溢出。如果我不得不猜测,我会说也许我可以消除更多的路径以加快速度,但我不确定如何在不消除最佳路径的情况下做到这一点

我是编程新手,所以我的代码中有一个简单的错误,或者我对算法有一个基本的误解?我相当自信我的帮助函数按预期工作,问题出在solve函数中。任何建议将不胜感激

import math
import copy

#Making move on board
def makeMove(board, move):
    L = int(math.sqrt(len(board)))
    copyBoard = copy.copy(board)
    zI = copyBoard.index(L**2-1)
    #Calculating move index in 1D list based off of x/y coords
    moveI = move[0] + L*move[1]
    copyBoard[zI], copyBoard[moveI] = copyBoard[moveI], copyBoard[zI]
    return copyBoard

#Function to find the board based off of the given path
def makeMoves(board, path):
    newBoard = copy.deepcopy(board)
    for move in path:
        newBoard = makeMove(newBoard, move)
    return newBoard

def mDist(board): #Calculating manhattan distance of board
    totalDist = 0
    L = int(math.sqrt(len(board)))
    for i in range(int(L**2)):
        #Finding sum of differences between x and y coordinates
        cX, cY = i % L, i // L
        fX, fY = board[i] % L, board[i] // L
        dX, dY = abs(cX-fX), abs(cY - fY)
        totalDist += (dX+dY)
    return totalDist

def nDisp(board):
    score = 0
    for i in range(len(board)):
        if i != board[i]:
            score += 1
    return score

def getLegalMoves(board):
    finalMoves = []
    L = int(math.sqrt(len(board)))
    #Finding the coordinates of the blank
    zI = board.index(L**2-1)
    zX, zY = zI % L, zI // L
    #Finding possible moves from that blank
    testMoves = [[zX+1, zY], [zX-1, zY], [zX, zY+1], [zX, zY-1]]
    for move in testMoves:
        if isLegalMove(move, L):
            finalMoves.append(move)
    return finalMoves

def isLegalMove(move, L):
    #Move is legal if on board
    for i in move:
        if i < 0 or i >= L:
            return False
    return True

def solve(board):
    queue = [] #List of paths, where a path is a sequence of moves
    for move in getLegalMoves(board): #Getting initial paths
        queue.append([move])
    def search(queue, board):
        bestScore = math.inf
        bestPath = None
        for path in queue:
            #Score based off of A* = estimated distance to finish + current distance
            dist = mDist(makeMoves(board, path))
            score = dist + len(path)
            #Checking if solved
            if dist == 0:
                return path
            #Finding best path that has not already been expanded
            if score < bestScore:
                bestScore = score
                bestPath = path
        #Removing the path since it is going to be expanded
        queue.remove(bestPath)
        bestPathBoard = makeMoves(board, bestPath)
        #Expanding the path
        for move in getLegalMoves(bestPathBoard):
            newPath = bestPath + [move]
            queue.append(newPath)
        #Recursing
        return search(queue, board)
    return search(queue, board)

print(solve([8, 0, 1, 6, 7, 3, 2, 5, 4]))

0 个答案:

没有答案