Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 实现递归回溯以生成迷宫_Python_Recursive Backtracking - Fatal编程技术网

Python 实现递归回溯以生成迷宫

Python 实现递归回溯以生成迷宫,python,recursive-backtracking,Python,Recursive Backtracking,我试图创建一个递归的createmaze函数,但是,我被卡住了,因为我不知道如何递归调用它并放置墙 有人能告诉我如何编辑代码使其工作吗?谢谢 编辑:因为我没有添加迷宫类,所以我想我应该添加它来帮助查看整个代码 class Maze: def __init__(self, Width, Height): assert Width>= 1 and Height>= 1 self.Width= Width self.Height=

我试图创建一个递归的createmaze函数,但是,我被卡住了,因为我不知道如何递归调用它并放置墙

有人能告诉我如何编辑代码使其工作吗?谢谢

编辑:因为我没有添加迷宫类,所以我想我应该添加它来帮助查看整个代码

class Maze:
    def __init__(self, Width, Height):
        assert Width>= 1 and Height>= 1

        self.Width= Width
        self.Height= Height
        self.board = np.zeros((Width, Height), dtype=WALL_TYPE)
        self.board.fill(EMPTY)

    def set_borders(self):
        self.board[0, :] = self.board[-1, :] = WALL
        self.board[:, 0] = self.board[:, -1] = WALL

    def is_wall(self, x, y):
        assert self.in_maze(x, y)
        return self.board[x][y] == WALL

    def set_wall(self, x, y):
        assert self.in_maze(x, y)
        self.board[x][y] = WALL

def create_maze(Width, Height, seed=None):
        Width = (Width // 2) * 2 + 1
        Height = (Height // 2) * 2 + 1

        if seed is not None:
            np.random.seed(seed)

        maze = Maze(Width, Height)
        maze.set_borders()

        x, y = rand(0, Width // 2) * 2, rand(0, Height // 2) * 2
        maze.set_wall(x, y)

        visited = []

        visited.append((x,y))

        while len(visited):
            start = visited[-1]

            if maze.in_maze(x - 2, y):
                visited.append((x - 2, y))

            if maze.in_maze(x + 2, y):
                visited.append((x + 2, y))

            if maze.in_maze(x, y - 2):
                visited.append((x, y - 2))

            if maze.in_maze(x, y + 2):
                visited.append((x, y + 2))

            visited.remove(start) # This goes somewhere but I don't know where

            if not maze.is_wall(x,y):
                maze.set_wall(x,y)


        create_maze() #recurse?
初始问题 好的,首先,你通常不会像这样设置递归函数。由于需要反复调用同一个函数,因此不希望每次调用都重新创建迷宫对象。您希望在递归函数调用之外执行所有设置和计算,并且只递归地执行一个非常特定的作业

安装程序 我假设迷宫类的设置有点像这样:

随机输入 班级迷宫: 定义初始自身、宽度、高度: self.width=width//2*2+1 self.height=height//2*2+1 这将为迷宫数据False:path、True:wall创建一个二维数组 self.cells=[ [对于rangeself.width中的x为真] 对于y,在自身高度范围内 ] def set_路径自身,x,y: self.cells[y][x]=假 def设置_wallself,x,y: self.cells[y][x]=真 思考迷宫的方法 好的,现在我可以开始递归生成本身了。现在,我不再采取加墙的方法,而是用墙填满整个迷宫,然后自己挖路径

在我们的迷宫中,尽管我们将其视为一个简单的开/关二维网格,带有墙和路径,但将其更像是一系列节点连接和它们之间的链接是很有帮助的。我可以看到你开始实施这个方法,你确保你的迷宫宽度和高度是奇数,例如宽度//2*2+1,你只选择了偶数单元格,尽管我不知道为什么

下面是一个快速图表,可以直观地看出我的意思:

每个红色圆圈都是一个节点,正如您所看到的,每个奇数块都包含一个节点,并且始终是一个路径块。我们将自动假定每个奇数块将包含一条路径,因此包含一个节点。这意味着,当我们生成迷宫时,当我们挖通通道时,我们将一次移动两个细胞,这样我们就可以在节点和灰色细胞之间创建链接

算法 我将实现的算法的本质如下:

沿着随机的方向移动,挖掘前进的道路,跟踪我去过的地方 重复,直到我走到尽头 “回溯”我去过的地方,直到我找到一条至少有一条可行路径的路径。转到步骤1 以下是更详细的相同步骤:

沿着随机的方向移动,跟踪我去过的地方

1.1。我们环顾每个方向,看看我们的移动选项在哪里

1.2。在存在有效路径的位置选择一个随机方向

1.3。移动到新节点记住,我们移动了两个单元格

重复,直到我走到尽头

2.1。继续重复第一步中的过程

2.2。如果在第一步中,您没有找到任何路径选项,则表明您已进入死胡同

回顾我去过的地方

3.1。按照您的足迹回溯以前访问过的节点

3.2。重复此操作,直到找到至少有一条未访问路径的节点为止

3.3。转到第1步,如中所示,开始在新路径中沿随机方向行走

现在使用递归函数实现这些步骤。通过移动两个单元格到新节点的每一步都将是一个新的函数调用,使用新的x-y坐标。以下是相同的步骤,但是递归的:

沿着任意方向移动,跟踪我去过的地方

1.1。随机选择一个方向,并检查它是否还没有被访问过(例如,如果我们已经沿着它走了,我们会已经挖通了,所以它将是一条路径)。因此,选择任何一个方向,这是一堵墙,如未访问

1.2。现在沿该方向移动两个单元,但请记住将两个节点之间的节点单元和链接单元都设置为路径,否则您就跳过了一堵墙。记住,当“移动”到新单元时,我们将使用新节点的x-y坐标再次调用该函数

重复,直到你到达一个死胡同

2.1。如果在第一步中,您发现所有方向都包含路径(例如,您已经访问了该节点的每个方向),则需要回溯

2.2。现在回溯一下,我们将退出当前函数调用。这意味着我们正在向后移动到先前的函数,该函数最初将我们移动到当前节点

回溯直到找到一条路径

3.1。退出函数调用后,您现在已使用先前的x-y坐标移回上一个节点。现在进入第一步,在那里寻找潜在的方向,如果没有,则进入第二步,进一步回溯

代码 好了,所有的理论和计划都完成了,我们现在可以把我们的设计实现成代码了

我将创建递归函数作为迷宫类的方法,如下所示:

班级迷宫: ... 构造函数和其他方法都在这里 ... def创建_mazeself,x,y: 我们的递归函数在这里 这意味着要完全创建我们的迷宫,我们称之为maze.create_maze1,1替换1,1,不管你的起始坐标是什么

因此,让我们来浏览一下我们前面设计的每个步骤,并将它们转换成代码

1.1选择一个随机可行的方向

def创建_mazeself,x,y: 将当前单元格设置为路径,这样我们以后就不会返回此处 self.set_路径self,x,y 我们以随机顺序创建一个方向列表,我们可以尝试 所有方向=[[1,0]、-1,0]、[0,1]、[0,1]、[0,1]] 随机.shuffl\u方向 我们不断尝试列表中的下一个方向,直到没有方向为止 当所有方向>0时: 我们删除并返回方向列表中的最后一项 方向到尝试=所有方向.pop 使用随机方向计算新节点的坐标。 当我们在每个方向上移动两个单元格到下一个节点时,我们使用*2 节点x=x+方向尝试[0]*2 节点y=y+方向尝试[1]*2 检查测试节点是否为墙(如未访问) 如果self.is_wallnode_x,node_y: 成功代码:我们找到了一条路 失败代码:我们找不到路径 如果当前单元格是墙,并且单元格在迷宫边界内,则返回的函数 def是_wallself,x,y: 检查坐标是否在迷宫网格内 如果0: 方向到尝试=所有方向.pop 节点x=x+方向尝试[0]*2 节点y=y+方向尝试[1]*2 如果self.is_wallnode_x,node_y: 成功代码:我们找到了一条路 将链接单元设置在从/移动到路径的两个节点之间 链接单元格\ux=x+方向\u到\utry[0] 链接单元格y=y+方向到尝试[1] self.set\u pathlink\u cell\u x,link\u cell\u y 移动到我们的新节点。请记住,我们每天都在调用该函数 我们移动的时间,所以我们再次调用它,但使用更新的x和y坐标 self.create_mazenode_x,node_y 失败代码:我们找不到路径 2.1。如果我们所有的方向都包含他们访问过的路径,那么我们需要回溯

2.2。为了回溯,我们退出函数调用,这会将我们带到上一个节点

结束函数的一种简单方法是调用return。这将停止在函数中运行任何其他代码,并返回到调用它的前一个函数

失败代码:我们找不到路径 回来 将其添加到递归函数中,它现在变成:

def创建_mazeself,x,y: self.set_路径x,y 所有方向=[[1,0]、-1,0]、[0,1]、[0,1]、[0,1]] 随机.shuffl\u方向 当所有方向>0时: 方向到尝试=所有方向.pop 节点x=x+方向尝试[0]*2 节点y=y+方向尝试[1]*2 如果self.is_wallnode_x,node_y: 链接单元格\ux=x+方向\u到\utry[0] 链接单元格y=y+方向到尝试[1] self.set\u pathlink\u cell\u x,link\u cell\u y self.create_mazenode_x,node_y 失败代码:我们找不到路径 回来 3.1。再次尝试步骤1,如果没有,请转至步骤2

基本上,我们现在已经编写了一个功能齐全的迷宫生成程序,尽我所能使用了一个相当好的递归方法。一旦函数到达死胡同,它将返回,返回到上一个函数,并继续该过程,直到该函数到达死胡同,等等

最终代码 随机输入 班级迷宫: 定义初始自身、宽度、高度: self.width=width//2*2+1 self.height=height//2*2+1 这将为迷宫数据False:path、True:wall创建一个二维数组 self.cells=[ [对于rangeself.width中的x为真] 对于y,在自身高度范围内 ] def set_路径自身,x,y: self.cells[y][x]=假 def设置_wallself,x,y: self.cells[y][x]=真 如果当前单元格是墙,则返回的函数, 如果细胞在迷宫的范围内 def是_wallself,x,y: 检查坐标是否在迷宫网格内 如果没有初始问题 好的,首先,你通常不会像这样设置递归函数。由于需要反复调用同一个函数,因此不希望每次调用都重新创建迷宫对象。您希望在递归函数调用之外执行所有设置和计算,并且只递归地执行一个非常特定的作业

安装程序 我假设你的迷宫课程设置是 有点像这样:

随机输入 班级迷宫: 定义初始自身、宽度、高度: self.width=width//2*2+1 self.height=height//2*2+1 这将为迷宫数据False:path、True:wall创建一个二维数组 self.cells=[ [对于rangeself.width中的x为真] 对于y,在自身高度范围内 ] def set_路径自身,x,y: self.cells[y][x]=假 def设置_wallself,x,y: self.cells[y][x]=真 思考迷宫的方法 好的,现在我可以开始递归生成本身了。现在,我不再采取加墙的方法,而是用墙填满整个迷宫,然后自己挖路径

在我们的迷宫中,尽管我们将其视为一个简单的开/关二维网格,带有墙和路径,但将其更像是一系列节点连接和它们之间的链接是很有帮助的。我可以看到你开始实施这个方法,你确保你的迷宫宽度和高度是奇数,例如宽度//2*2+1,你只选择了偶数单元格,尽管我不知道为什么

下面是一个快速图表,可以直观地看出我的意思:

每个红色圆圈都是一个节点,正如您所看到的,每个奇数块都包含一个节点,并且始终是一个路径块。我们将自动假定每个奇数块将包含一条路径,因此包含一个节点。这意味着,当我们生成迷宫时,当我们挖通通道时,我们将一次移动两个细胞,这样我们就可以在节点和灰色细胞之间创建链接

算法 我将实现的算法的本质如下:

沿着随机的方向移动,挖掘前进的道路,跟踪我去过的地方 重复,直到我走到尽头 “回溯”我去过的地方,直到我找到一条至少有一条可行路径的路径。转到步骤1 以下是更详细的相同步骤:

沿着随机的方向移动,跟踪我去过的地方

1.1。我们环顾每个方向,看看我们的移动选项在哪里

1.2。在存在有效路径的位置选择一个随机方向

1.3。移动到新节点记住,我们移动了两个单元格

重复,直到我走到尽头

2.1。继续重复第一步中的过程

2.2。如果在第一步中,您没有找到任何路径选项,则表明您已进入死胡同

回顾我去过的地方

3.1。按照您的足迹回溯以前访问过的节点

3.2。重复此操作,直到找到至少有一条未访问路径的节点为止

3.3。转到第1步,如中所示,开始在新路径中沿随机方向行走

现在使用递归函数实现这些步骤。通过移动两个单元格到新节点的每一步都将是一个新的函数调用,使用新的x-y坐标。以下是相同的步骤,但是递归的:

沿着任意方向移动,跟踪我去过的地方

1.1。随机选择一个方向,并检查它是否还没有被访问过(例如,如果我们已经沿着它走了,我们会已经挖通了,所以它将是一条路径)。因此,选择任何一个方向,这是一堵墙,如未访问

1.2。现在沿该方向移动两个单元,但请记住将两个节点之间的节点单元和链接单元都设置为路径,否则您就跳过了一堵墙。记住,当“移动”到新单元时,我们将使用新节点的x-y坐标再次调用该函数

重复,直到你到达一个死胡同

2.1。如果在第一步中,您发现所有方向都包含路径(例如,您已经访问了该节点的每个方向),则需要回溯

2.2。现在回溯一下,我们将退出当前函数调用。这意味着我们正在向后移动到先前的函数,该函数最初将我们移动到当前节点

回溯直到找到一条路径

3.1。退出函数调用后,您现在已使用先前的x-y坐标移回上一个节点。现在进入第一步,在那里寻找潜在的方向,如果没有,则进入第二步,进一步回溯

代码 好了,所有的理论和计划都完成了,我们现在可以把我们的设计实现成代码了

我将创建递归函数作为迷宫类的方法,如下所示:

班级迷宫: ... 构造函数和其他方法都在这里 ... def创建_mazeself,x,y: 我们的递归函数在这里 这意味着要完全创建我们的迷宫,我们称之为maze.create_maze1,1替换1,1,不管你的起始坐标是什么

因此,让我们来浏览一下我们前面设计的每个步骤,并将它们转换成代码

1.1选择一个随机可行的方向

def创建_mazeself,x,y: 将当前单元格设置为路径,这样我们以后就不会返回此处 self.set_路径self,x,y 我们以随机顺序创建一个方向列表,我们无法 雷 所有方向=[[1,0]、-1,0]、[0,1]、[0,1]、[0,1]] 随机.shuffl\u方向 我们不断尝试列表中的下一个方向,直到没有方向为止 当所有方向>0时: 我们删除并返回方向列表中的最后一项 方向到尝试=所有方向.pop 使用随机方向计算新节点的坐标。 当我们在每个方向上移动两个单元格到下一个节点时,我们使用*2 节点x=x+方向尝试[0]*2 节点y=y+方向尝试[1]*2 检查测试节点是否为墙(如未访问) 如果self.is_wallnode_x,node_y: 成功代码:我们找到了一条路 失败代码:我们找不到路径 如果当前单元格是墙,并且单元格在迷宫边界内,则返回的函数 def是_wallself,x,y: 检查坐标是否在迷宫网格内 如果0: 方向到尝试=所有方向.pop 节点x=x+方向尝试[0]*2 节点y=y+方向尝试[1]*2 如果self.is_wallnode_x,node_y: 成功代码:我们找到了一条路 将链接单元设置在从/移动到路径的两个节点之间 链接单元格\ux=x+方向\u到\utry[0] 链接单元格y=y+方向到尝试[1] self.set\u pathlink\u cell\u x,link\u cell\u y 移动到我们的新节点。请记住,我们每天都在调用该函数 我们移动的时间,所以我们再次调用它,但使用更新的x和y坐标 self.create_mazenode_x,node_y 失败代码:我们找不到路径 2.1。如果我们所有的方向都包含他们访问过的路径,那么我们需要回溯

2.2。为了回溯,我们退出函数调用,这会将我们带到上一个节点

结束函数的一种简单方法是调用return。这将停止在函数中运行任何其他代码,并返回到调用它的前一个函数

失败代码:我们找不到路径 回来 将其添加到递归函数中,它现在变成:

def创建_mazeself,x,y: self.set_路径x,y 所有方向=[[1,0]、-1,0]、[0,1]、[0,1]、[0,1]] 随机.shuffl\u方向 当所有方向>0时: 方向到尝试=所有方向.pop 节点x=x+方向尝试[0]*2 节点y=y+方向尝试[1]*2 如果self.is_wallnode_x,node_y: 链接单元格\ux=x+方向\u到\utry[0] 链接单元格y=y+方向到尝试[1] self.set\u pathlink\u cell\u x,link\u cell\u y self.create_mazenode_x,node_y 失败代码:我们找不到路径 回来 3.1。再次尝试步骤1,如果没有,请转至步骤2

基本上,我们现在已经编写了一个功能齐全的迷宫生成程序,尽我所能使用了一个相当好的递归方法。一旦函数到达死胡同,它将返回,返回到上一个函数,并继续该过程,直到该函数到达死胡同,等等

最终代码 随机输入 班级迷宫: 定义初始自身、宽度、高度: self.width=width//2*2+1 self.height=height//2*2+1 这将为迷宫数据False:path、True:wall创建一个二维数组 self.cells=[ [对于rangeself.width中的x为真] 对于y,在自身高度范围内 ] def set_路径自身,x,y: self.cells[y][x]=假 def设置_wallself,x,y: self.cells[y][x]=真 如果当前单元格是墙,则返回的函数, 如果细胞在迷宫的范围内 def是_wallself,x,y: 检查坐标是否在迷宫网格内
如果为0,是否需要关于递归回溯工作原理的教程?你的问题可能会在你的代码需要修复时出现,而据我所知,你需要帮助理解算法。我了解它是如何工作的,只是不知道如何实现它。这不是问题的一部分宽度=宽度//2*2+1->宽度|=1你需要关于递归回溯工作原理的教程吗?你的问题可能会在你的代码需要修复时出现,而据我所知,你需要帮助理解算法。我了解它是如何工作的,只是不知道如何很好地实现它。这不是问题的一部分宽度=宽度//2*2+1->宽度|=1伟大的解释!你可能想考虑在你的方法中修正这些错误,他们需要有自我作为第一个论点,特别是因为你明确地使用了自我。还可以公平地指出,print将使用_str______方法的结果,并且只有在未实现_str__的情况下才会返回到_repr____方法。这意味着uuu str_uuuu方法返回当前状态的可读版本,这就是您当前的uu repr_uuu方法所做的,并且u repr_uu应返回计算机对状态的表示。谢谢你
伙计!我对我的特殊要求有意见,但你的解释帮助我理解了应该改变什么。谢谢很好的解释!你可能想考虑在你的方法中修正这些错误,他们需要有自我作为第一个论点,特别是因为你明确地使用了自我。还可以公平地指出,print将使用_str______方法的结果,并且只有在未实现_str__的情况下才会返回到_repr____方法。这意味着uuu str_uuuu方法返回当前状态的可读版本,这就是您当前的uu repr_uuu方法所做的,并且u repr_uu应返回计算机对状态的表示。你真了不起,伙计!我对我的特殊要求有意见,但你的解释帮助我理解了应该改变什么。谢谢
>>> maze.create_maze(1, 1)
>>> print(maze)
██████████████████
██      ██      ██
██████  ██  ██  ██
██  ██  ██  ██  ██
██  ██  ██████  ██
██  ██          ██
██  ██████████  ██
██              ██
██████████████████
>>> maze.create_maze(4, 4)
          ██      
  ██████  ██████  
  ██              
  ██████████████  
  ██      ██      
  ██  ██████████  
  ██          ██  
  ██████████  ██  
              ██