【问题标题】:Maximum recursion depth exceeded in searching for a path搜索路径时超出最大递归深度
【发布时间】:2020-12-24 13:00:15
【问题描述】:

我正在尝试编写一个程序,该程序接受一个矩阵,该矩阵表示一个充满 0 和 1 的板。 目标是使用回溯找到从左上角位置到目标位置的路径。您一次只能向上、向下、向左和向右移动一个空间。下面的代码引发了

RecursionError: maximum recursion depth exceeded in comparison

为什么会导致错误?有没有办法同时向上、向下、向右和向左移动而不会导致此错误?

class Maze:
    def __init__(self, maze, target):
        self.x, self.y = target
        self.b = maze
        self.n = len(self.b)
        self.sb = [[0 for _ in range(self.n)] for _ in range(self.n)]

    def is_safe(self, row, col):
        if 0 <= row < self.n and 0 <= col < self.n and self.b[row][col] == 1:
            return True
        return False

    def find_path(self):
        move_x = [1, -1, 0, 0]
        move_y = [0, 0, 1, -1]

        if not self.find_path_rec(move_x, move_y, 0, 0):
            print("No path")
        else:
            self.print_maze()

    def find_path_rec(self, move_x, move_y, curr_x, curr_y):

        if curr_y == self.y and curr_x == self.x and self.b[self.x][self.y] == 1:
            self.sb[curr_x][curr_y] = 1
            return True

        for i in range(4):
            new_x = move_x[i] + curr_x
            new_y = move_y[i] + curr_y
            if self.is_safe(new_x, new_y):
                self.sb[new_x][new_y] = 1

                if self.find_path_rec(move_x, move_y, new_x, new_y):
                    return True

                self.sb[new_x][new_y] = 0

        return False

    def print_maze(self):
        for i in range(self.n):
            for j in range(self.n):
                print(self.sb[i][j], end="")
            print()


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

【问题讨论】:

  • 要了解递归,需要了解递归。对了,你查self.sb吗?

标签: python recursion path-finding


【解决方案1】:

维基百科迷宫生成算法页面https://en.wikipedia.org/wiki/Maze_generation_algorithm有你的答案

[递归] 方法的一个缺点是递归深度大——在最坏的情况下,例程可能需要在正在处理的区域的每个单元格上递归,这可能超过许多环境中的最大递归堆栈深度。

替代方法是使用堆栈来存储到目前为止的路径,而不是使用递归。将每个动作附加到堆栈中,并在遇到死胡同时回溯。

【讨论】:

    【解决方案2】:

    我认为问题出在这一行:

    if self.is_safe(new_x, new_y):
    

    应该是:

    if self.is_safe(new_x, new_y) and self.sb[new_x][new_y] == 0:
    

    也就是说,不要重新访问位置,因为你会进入圆周运动并因此递归堆栈溢出。以下是我自己对您的问题的思考对您的代码的修改:

    COL, ROW = (0, 1)
    
    class Maze:
        def __init__(self, maze, target):
            self.target = target
            self.maze = maze
            self.visited = set()
    
        def is_safe(self, position):
            col, row = position
            return 0 <= row < len(self.maze) and 0 <= col < len(self.maze[row]) and self.maze[row][col] == 1
    
        def find_path(self):
            start = (0, 0)
            moves = [(1, 0), (-1, 0), (0, 1), (0, -1)]
    
            self.visited.add(start)
    
            return self.find_path_recursive(moves, start)
    
        def find_path_recursive(self, moves, current):
    
            if current == self.target and self.maze[self.target[ROW]][self.target[COL]] == 1:
                return True
    
            for move in moves:
                new = move[COL] + current[COL], move[ROW] + current[ROW]
    
                if self.is_safe(new) and new not in self.visited:
                    self.visited.add(new)
    
                    if self.find_path_recursive(moves, new):
                        return True
    
                    self.visited.remove(new)
    
            return False
    
        def print_maze(self):
            for row in range(len(self.maze)):
                for col in range(len(self.maze[row])):
                    print(1 if (col, row) in self.visited else 0, end="")
                print()
    
    if __name__ == '__main__':
    
        maze = [
            [1, 1, 1, 1],
            [1, 1, 0, 1],
            [1, 0, 0, 1],
            [1, 1, 0, 1]
        ]
    
        game = Maze(maze, (3, 3))
    
        if game.find_path():
            game.print_maze()
        else:
            print("No path")
    

    我的坐标感可能与你的不同,所以在评估结果时要考虑到这一点。

    输出

    > python3 test.py
    1111
    0001
    0001
    0001
    >
    

    如果我们将目标移动到(1, 3),那么我们得到:

    > python3 test.py
    1100
    1100
    1000
    1100
    >
    

    我还更改了您的逻辑以可能处理矩形迷宫,而不仅仅是方形迷宫。

    【讨论】:

      【解决方案3】:

      错误在这个函数中:

      def is_safe(self, row, col):
          if 0 <= row < self.n and 0 <= col < self.n and self.b[row][col] == 1:
              return True
          return False
      

      条件应该包括检查单元格尚未被访问:

          if (0 <= row < self.n and 0 <= col < self.n and self.b[row][col] == 1 
                                and self.sb[row][col] == 0):
       #                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
      

      我还建议使用有意义的名称:

      • self.b 最好命名为 self.maze
      • self.sb 最好命名为 self.visited

      【讨论】:

      • 我不同意你的修正。起始位置在1,目标被验证为1,因此这些是开放路径,而零是墙壁。您的修复程序直接撞到了墙上。不过,我同意您的变量名更改。
      • 抱歉,我误读了您的代码(与bsb 混淆)- 表明如果变量名称选择不当,代码是多么难以被另一双眼睛阅读;)我会一分钟后更新。
      • 更新了我的答案。旧条件又回来了,但我添加了我打算添加的内容。
      • 您有什么意见吗?
      猜你喜欢
      • 1970-01-01
      • 2013-12-01
      • 2017-03-24
      • 2011-12-31
      • 2017-08-09
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多