簡體   English   中英

N-puzzle a-star python求解器效率

[英]N-puzzle a-star python solver efficiency

我已經寫了一個a-star算法,旨在解決滑塊/ n難題。 它適用於小難題,但隨着復雜性的增加而苦苦掙扎。

我已經實現了幾種提高效率的方法(heapq等),但是我已經結束了我的想法。 您能想到我能做些其他什么來改善它嗎?

我的代碼在這里: https : //repl.it/@Jaspirian/SimilarWoodenMemoryallocator

一些重要的部分:

啟發式:

def heuristic_estimate_manhattan(self, other):
    """
    Finds the heuristic estimation of the cost to reach another state from this one.
    This heuristic is based on "manhattan distance."
    """
    estimate = 0

    for index in range(len(self.blocks)):
        estimate += abs(other.blocks[index][0] - self.blocks[index][0]) + abs(other.blocks[index][1] - self.blocks[index][1])

    return estimate

鄰居函數:

def get_neighbors(self, previous):
    """
    Gets all adjacent neighbors of the state, minus the previous.
    This function gives 7 neighbors: 4 orthogonal, 4 diagonal, with the previous state trimmed.
    """
    neighbors = []

    moves = ((-1,0),(1,0),(0,-1),(0,1))
    zeroLoc = self.blocks[0]

    for move in moves:
        # swap 0 and whatever
        newBlocks = copy.deepcopy(self.blocks)
        newZeroLoc = (zeroLoc[0] + move[0], zeroLoc[1] + move[1])
        # skip this state if we've moved off the board
        if newZeroLoc[0] < 0 or newZeroLoc[1] < 0 or newZeroLoc[0] > self.width-1 or newZeroLoc[1] > self.height-1:
            # print("we've moved off the board.")
            continue
        # skip this state if it's the same as the previous
        if previous and previous.blocks[0] == newZeroLoc:
            # print("this is just the same!")
            continue

        # move the 0
        newBlocks[0] = newZeroLoc

        # move whatever's in that location...
        # to the previous one
        for face, location in newBlocks.items():
            if face != 0 and location == newZeroLoc:
                newBlocks[face] = zeroLoc

        neighbor = Block_Puzzle(newBlocks)
        neighbors.append(neighbor)

    return neighbors

a-star算法:

def aStar(start, goal):
"""
A star search algorithm. Takes a start state and an end state.
While there are available moves, loops through them and exits if the end is found.
Returns the list of states that are the "quickest" way to the end.
"""
...
openHeap = [start]
heapq.heapify(openHeap)
...
# While there are yet nodes to inspect,
while(len(openHeap) > 0):
    # Pop the lowest f-score state off. 
    current = heapq.heappop(openHeap)

    # print(len(openHeap))

    # If we've reached the goal:
    if current == goal:
        # return the list of states it took to get there.
        ...
        return path

    # make sure we won't visit this state again.
    closedDict[current] = True

    # For each possible neighbor of our current state,
    for neighbor in current.get_neighbors(cameFrom.get(current)):
        # Skip it if it's already been evaluated
        if neighbor in closedDict:
            continue

        # Add it to our open heap
        heapq.heappush(openHeap, neighbor)

        tentative_gScore = gScore[current] + 1
        # If it takes more to get here than another path to this state, skip it.
        if tentative_gScore >= gScore[neighbor]:
            continue

        # If we got to this point, add it!
        cameFrom[neighbor] = current
        gScore[neighbor] = tentative_gScore
        fScore[neighbor] = gScore[neighbor] + neighbor.heuristic_estimate_manhattan(goal)

return None

如果我正確理解, self.blocks是一本字典。 您可以使用復制

newBlocks = self.blocks.copy()  # or: dict(self.blocks)

節省一些時間。 在此不需要deepcopy

get_neighbors ,在這些檢查(例如邊界檢查)期間不使用newBlocks 如果這些檢查中的任何一個失敗,則deepcopy() (或由jsmolka回答的常規copy() )將浪費時間。 您可以在檢查將該副本移動到。


在算法本身中,我建議將試探法乘以略大於1的數字。例如:

fScore[neighbor] = gScore[neighbor] + 1.0001 * neighbor.heuristic_estimate_manhattan(goal)

這應該自動執行平局決勝的方式,使我們更喜歡成本主要為g (實際成本,可靠信息,已知是正確的)的路徑,而不是那些較大的相同總成本f確定的路徑。啟發式h (啟發式,猜測,可能不完全正確/可靠)。 通常,這是A *的最佳決勝局。 從理論上講,該乘法可能會使您的試探法不被接受,但是如果乘法器足夠接近1.0 ,那就沒關系了。


假設current有一個得分f_current ,和剛剛彈出的的openHeap 假設新近產生的鄰居最終獲得完全相同的f分數(現在只是更大的g分量和較小的h分量)。 您肯定知道,在下一次迭代中,具有此分數的節點將立即再次彈出。 這意味着將其實際推入堆然后再次彈出是無效的。

還有一個單獨的(未排序的)堆棧也更有效。 如果f分數等於父代的f分數,則將節點推入此堆棧而不是堆中。 如果此堆棧是非空的,則始終從中彈出節點, 而不是從堆中彈出節點。 如果此堆棧為空,則僅從堆中彈出。

注意 :與上述基於乘法的平局決勝相結合,實現此想法變得很復雜。 如果你可以手動指定為您堆排序標准,還可以實現平局決勝不同(如明確治療基礎上等於節點f得分,如果它具有更大的較小的g /小h )。

Dennis和jsmolka都對我有所幫助,但是我的代碼的致命缺陷就在這里:

# Add it to our open heap
    heapq.heappush(openHeap, neighbor)

    ...

    # If we got to this point, add it!
    cameFrom[neighbor] = current
    gScore[neighbor] = tentative_gScore
    fScore[neighbor] = gScore[neighbor] + neighbor.heuristic_estimate_manhattan(goal)

我的理解是,將對象的lt()函數推入堆時會調用它。 如果是這種情況,我會將狀態推送到堆中,然后再更改值,為時已晚,無法更改順序。

我對本節進行了重新設計,現在相同的難題所需的時間為5.3秒,而jsmolka和Dennis的幫助則為86秒,而幫助之前為250秒。

完成的代碼在此處 ,相關部分在下面。

for neighbor in current.get_neighbors(cameFrom.get(current)):
        # Skip it if it's already been evaluated
        if neighbor in closedSet:
            continue

        tentative_gScore = gScore[current] + 1
        # If this path costs less than previous paths here...
        if tentative_gScore < gScore[neighbor]:
            # Update the values for this state.
            cameFrom[neighbor] = current
            gScore[neighbor] = tentative_gScore
            fScore[neighbor] = gScore[neighbor] + (1.0001 * neighbor.heuristic_estimate_manhattan(goal))

        # Finally, add it to our open heap
        heapq.heappush(openHeap, neighbor)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM