迷宫寻路实现(BFS)没有给出正确的路径

时间:2021-04-16 10:55:57

标签: python algorithm breadth-first-search path-finding maze

我正在尝试为带球的迷宫获得最短路径:球一直滚动直到撞到墙壁。我使用 Dijkstra 算法将 heapq 用于优先队列。但是,我得到了一条非最佳路径。

这是我的示例输入代码:

maze = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
        [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
        [0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0],
        [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0],
        [0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0],
        [0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0],
        [1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
        [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1],
        [1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
        [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
        [0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0],
        [0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0],
        [0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
        [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

start = (0, 0)
end = (22, 22)

def shortestDistance(maze: List[List[int]], start: List[int], destination: List[int]):
    start, destination = tuple(start), tuple(destination)
    row, col = len(maze), len(maze[0])
    moves = [(-1, 0), (0, 1), (0, -1), (1, 0)]
    dstr = ['u', 'r', 'l', 'd']

    class Point:
        def __init__(self, distance, coordinates, directions):
            self.distance = distance
            self.coordinates = coordinates
            self.directions = directions

        def __eq__(self, p):
            if self.distance == p.distance:
                return self.__lt__(self, p)

            return self.distance - p.distance

        def __lt__(self, p):
            return len(self.directions) - len(p.directions)


    heap = [(Point(0, start, ""))]
    visited = set()
    while heap:
        point = heapq.heappop(heap)
        dist = point.distance
        node = point.coordinates
        directions = point.directions

        if node in visited: continue
        if node == destination:
            return directions

        visited.add(node)

        for idx, move in enumerate(moves):
            dx, dy = move
            newX = node[0]
            newY = node[1]
            distance = dist
            newDirections = directions
            while 0 <= newX + dx < row and 0 <= newY + dy < col and maze[newX + dx][newY + dy] == 0:
                newX += dx
                newY += dy
                distance += 1
                if (newX, newY) == destination:
                    break

            if (newX, newY) not in visited:
                heapq.heappush(heap, Point(distance, (newX, newY), newDirections + dstr[idx]))

    return "Impossible"


path = shortestDistance(maze, start, end)
print(path)

这个想法是比较距离,如果相等,选择方向变化较少的路径。

我目前正在获取 rdrludlrdrudludldldr(即右下右左...)作为输出,但在索引 2 处找到的序列“rl”没有意义:“Right”应该后面不能跟“左”,也不应该跟“上”跟“下”,反之亦然。这样的顺序显然不是最佳的,因为可以省略这两个移动中的第一个,以使球在同一位置并移动更短的距离。

这个迷宫的预期输出是drururdrdrurdrd

为什么我没有得到最短路径?

1 个答案:

答案 0 :(得分:2)

问题在于 __lt__ 函数没有做它应该做的事情。

它应该返回一个布尔值,当 self 被认为小于 p 时为真。当您当前返回一个整数结果时,该结果通常是非零的,您会遇到这样的情况:一对 (p, q) 点将 p < q 和 q < p 都为真...这会导致不稳定的行为。

以下是您如何定义它:

def __lt__(self, p):
    return ((self.distance, len(self.directions), self.directions) < 
          < (p.distance, len(p.directions), p.directions)) 

通过此更改返回的路径是

rdrdldldrdr

简化

您可以使用命名元组,而不是创建类 Point,这使一切变得更容易(更快)。您只需要更改“属性”的顺序,以便这些点以所需的方式进行比较,即 directions 应该在 coordinates 之前,方向字符串的长度应该有自己的属性:

from collections import namedtuple

# change order of properties so comparison works as intended
Point = namedtuple("Point", "distance, length, directions, coordinates")

然后在调用 Point 的地方进行适当的更改:

heap = [Point(0, 0, "", start)]
# ...
heapq.heappush(heap, Point(distance, len(newDirections) + 1, newDirections + dstr[idx], (newX, newY)))