【问题标题】:Solving a maze using recursion in python在python中使用递归解决迷宫
【发布时间】:2014-05-07 06:45:03
【问题描述】:

所以,我有一个任务要求我使用递归解决一个迷宫。我将发布作业指南,以便您可以看到我在说什么。教授没有过多地解释递归,他给了我们递归的例子,我将发布,但我希望有人能够给我一个更深入的递归解释,以及我将如何应用它来解决一个迷宫。我不是要求任何人编写代码,我只是希望一些解释能让我走上正确的道路。感谢任何回答的人。

以下是我的示例:

    def foo():
        print("Before")
        bar()
        print("After")

    def bar():
        print("During")


    def factorial(n):
        """n!"""
        product = 1
        for i in range(n,0,-1):
        product *= i
        return product

    def recFac(n):
        """n! = n * (n-1)!"""
        if(n == 1):
          return 1
        return n * recFac(n-1)

    def hello():
        """Stack overflow!"""
        hello()

    def fib(n):
        """f(n) = f(n-1) + f(n-2)
        f(0) = 0
        f(1) = 1"""
        if n == 0 or n == 1: #base case
           return n
        return fib(n-1) + fib(n-2) #recursive case

    def mult(a,b):
        """a*b = a + a + a + a ..."""
        #base case
        if (b == 1):
           return a
        #recursive case
        prod = mult(a,b-1)
        prod *= a
        return prod


    def exp(a,b):
        """a ** b = a* a * a * a * a *.... 'b times'"""
        #base case
        if (b==0):
           return 1
        if (b == 1):
           return a
        #recursive case
        return exp(a,b-1)*a

    def pallindrome(word):
        """Returns True if word is a pallindrome, False otherwise"""
        #base case
        if word == "" or len(word)==1:
           return True

        #recursive case
        if word[0] == word[len(word)-1]:
        word = word[1:len(word)-1]
        return pallindrome(word)
        else:
            return False

以下是指南:

你将创建一个迷宫爬虫,它能够利用递归的力量解决你给它的任何迷宫!

问题 1 - 加载迷宫

在解决迷宫之前,您必须先加载它。对于此作业,您将为迷宫使用简单的文本格式。您可以使用此示例迷宫或创建您自己的迷宫。

这个问题的目标是加载任何给定的迷宫文件,并将其读入二维列表。 例如:loadMaze("somemaze.maze") 应该加载 somemaze.maze 文件并创建如下列表...

    [['#','#','#','#','#','#','#','#','#'], 
     ['#','S','#',' ',' ',' ','#','E','#'], 
     ['#',' ','#',' ','#',' ',' ',' ','#'], 
     ['#',' ',' ',' ','#',' ','#',' ','#'], 
     ['#', #','#','#','#','#','#','#','#']] 

请注意,列表已删除所有“\r”和“\n”字符。为了使下一个问题更简单,您可以将此列表设为全局变量。

接下来编写一个以更好的格式打印出迷宫的函数:

例如,

    ####################################
    #S#  ##  ######## # #      #     # #
    # #   #             # #        #   #
    #   # ##### ## ###### # #######  # #
    ### # ##    ##      # # #     #### #
    #   #    #  #######   #   ###    #E#
    ####################################

在继续之前用不同的迷宫测试你的代码。

问题 2 - 准备解决迷宫

在解决迷宫之前,您需要找到起点!在代码中添加一个名为 findStart() 的函数,它将搜索迷宫(逐个字符)并返回“S”字符的 x 和 y 坐标。您可以假设迷宫中最多存在一个这样的角色。如果在迷宫中没有找到“S”,则 x 和 y 坐标都返回 -1。

在继续之前在多个位置(包括无位置)使用“S”测试您的代码。

问题 3 - 解决迷宫!

最后,你准备好递归解决迷宫了!您的解决方案应该只需要一个方法:solve(y,x)

solve 方法的单个实例应该解决迷宫中的单个位置。参数 y 和 x 是要求解的当前坐标。您的解决方法应该完成一些事情。它应该检查它当前是否正在解决“E”的位置。在这种情况下,您的求解方法已成功完成。否则它应该尝试递归解决右边的空间。请注意,您的方法应该只尝试解决空间,而不是墙壁('#')。如果该递归没有导致结束,则尝试向下,然后向左,然后向上。如果一切都失败了,你的代码应该回溯一个步骤,并尝试另一个方向。

最后,在解决迷宫问题时,您的代码应该留下进度指示符。如果它正在向右搜索,则当前位置应该有一个“>”来代替空白空间。如果搜索下来放一个'v'。如果向左搜索'

一旦你的迷宫被解决,再次打印出迷宫。您应该查看走迷宫的分步指南。 例如,

    main("somemaze.maze")
    ######### 
    #S#   #E# 
    # # #   # 
    #   # # # 
    #########

S 在 (1,1)

     ######### 
     #S#>>v#E# 
     #v#^#>>^# 
     #>>^# # # 
     #########

使用不同的开始和结束位置测试您的代码,并且可以选择在各种迷宫中进行测试。

这是我目前的代码: 但是代码实际上并没有打印迷宫中的轨道,我不知道为什么。

    def loadMaze():
        readIt = open('Maze.txt', 'r')
        readLines = readIt.readlines()
        global mazeList
        mazeList = [list(i.strip()) for i in readLines]

    def showMaze():
        for i in mazeList:
            mazeprint = ''
        for j in i:
            mazeprint = mazeprint + j
        print(mazeprint)
        print('\n')    

    def solve(x,y, mazeList):
        mazeList[x][y] = "o"
        #Base case  
        if y > len(mazeList) or x > len(mazeList[y]):
           return False
        if mazeList[y][x] == "E":
           return True 
        if mazeList[y][x] != " ":
           return False
        #marking
        if solve(x+1,y) == True:  #right
           mazeList[x][y]= '>'
        elif solve(x,y+1) == True:  #down
             mazeList[x][y]= 'v'     
        elif solve(x-1,y) == True:  #left
             mazeList[x][y]= '<'     
        elif solve(x,y-1) == True:  #up
             mazeList[x][y]= '^'
        else:
           mazeList[x][y]= ' '
        return (mazeList[x][y]!= ' ')

【问题讨论】:

  • 谢谢!我一直在寻找示例,但我没有找到对我真正有用的示例!这个似乎很有帮助。
  • 没问题。如果您遇到任何其他问题,只需使用您的新查询更新问题
  • 与迷宫解决没有直接关系,但我在过去几天尝试在这里stackoverflow.com/a/22678614/478656 展示/解释一个递归问题。
  • 有人能告诉我为什么我更新的代码可能无法正常工作吗?

标签: python recursion maze


【解决方案1】:

(约会我自己,实际上我在 COBOL 中做这个问题,在高中。)

您可以将解决迷宫视为采取步骤。

当您迈出一步时,每次都适用相同的规则。因为每次都应用相同的规则,所以您可以对每个步骤使用完全相同的说明集。当您迈出一步时,您只需再次调用相同的例程,更改参数以指示新的步骤。那是递归。一步一步解决问题。

注意:一些递归解决方案将问题分成两半,解决每一半独立于另一半,当两个解决方案实际上是独立的时有效。它在这里不起作用,因为每个步骤(解决方案)都取决于前面的步骤。

如果你遇到了死胡同,你就会退出死胡同,直到你找到一个仍有可行方格可供检查的步骤。

有用的提示:你没有在出口的路上标记正确的路径,因为你不知道你现在正在采取的步骤是通往出口的路径的一部分出口。当您知道每一步确实是路径的一部分时,您标记路径在返回的路上。您可以这样做,因为每一步都会记住在进行下一步之前它所在的方格。

相反,您在您尝试过的每个方格上都打了一个标记,上面只写着:我来过这里,不需要再检查一次。在打印解决方案之前清理它们。

【讨论】:

    【解决方案2】:

    这是我对 CodeEval 的 The Labirynth 挑战的解决方案:

    import sys
    sys.setrecursionlimit(5000)
    
    
    class Maze(object):
        FLOOR = ' '
        WALLS = '*'
        PATH = '+'
    
        def __init__(self):
            self.cols = 0
            self.rows = 0
            self.maze = []
    
        def walk_forward(self, current_k, r, c):
            self.maze[r][c] = current_k
            next_k = current_k + 1
            # up
            if r > 1:
                up = self.maze[r - 1][c]
                if up != self.WALLS:
                    if up == self.FLOOR or int(up) > current_k:
                        self.walk_forward(next_k, r - 1, c)
            # down
            if r < self.rows - 1:
                down = self.maze[r + 1][c]
                if down != self.WALLS:
                    if down == self.FLOOR or int(down) > current_k:
                        self.walk_forward(next_k, r + 1, c)
            # left
            if c > 1:
                left = self.maze[r][c - 1]
                if left != self.WALLS:
                    if left == self.FLOOR or int(left) > current_k:
                        self.walk_forward(next_k, r, c - 1)
            # right
            if c < self.cols - 1:
                right = self.maze[r][c + 1]
                if right != self.WALLS:
                    if right == self.FLOOR or int(right) > current_k:
                        self.walk_forward(next_k, r, c + 1)
    
        def walk_backward(self, r, c):
            current_k = self.maze[r][c]
            if not isinstance(current_k, int):
                return False
            self.maze[r][c] = self.PATH
    
            up = self.maze[r - 1][c] if r > 0 else None
            down = self.maze[r + 1][c] if r < self.rows - 1 else None
            left = self.maze[r][c - 1] if c > 1 else None
            right = self.maze[r][c + 1] if c < self.cols else None
    
            passed = False
            if up and isinstance(up, int) and up == current_k - 1:
                self.walk_backward(r - 1, c)
                passed = True
            if down and isinstance(down, int) and down == current_k - 1:
                self.walk_backward(r + 1, c)
                passed = True
            if left and isinstance(left, int) and left == current_k - 1:
                self.walk_backward(r, c - 1)
                passed = True
            if right and isinstance(right, int) and right == current_k - 1:
                self.walk_backward(r, c + 1)                    
    
        def cleanup(self, cleanup_path=False):
            for r in range(0, self.rows):
                for c in range(0, self.cols):
                    if isinstance(self.maze[r][c], int):
                        self.maze[r][c] = self.FLOOR
                    if cleanup_path and self.maze[r][c] == self.PATH:
                        self.maze[r][c] = self.FLOOR
    
        def solve(self, start='up', show_path=True):
            # finding start and finish points
            upper = lower = None
            for c in range(0, self.cols):
                if self.maze[0][c] == self.FLOOR:
                    upper = (0, c)
                    break
            for c in range(0, self.cols):
                if self.maze[self.rows - 1][c] == self.FLOOR:
                    lower = (self.rows - 1, c)
                    break
            if start == 'up':
                start = upper
                finish = lower
            else:
                start = lower
                finish = upper
    
            self.cleanup(cleanup_path=True)
            self.walk_forward(1, start[0], start[1])
            length = self.maze[finish[0]][finish[1]]
            if not isinstance(length, int):
                length = 0
            if show_path:
                self.walk_backward(finish[0], finish[1])
                self.cleanup(cleanup_path=False)
            else:
                self.cleanup(cleanup_path=True)
            return length
    
        def save_to_file(self, filename):
            with open(filename, 'w') as f:
                f.writelines(str(self))
    
        def load_from_file(self, filename):
            self.maze = []
            with open(filename, 'r') as f:
                lines = f.readlines()
            for line in lines:
                row = []
                for c in line.strip():
                    row.append(c)
                self.maze.append(row)
            self.rows = len(self.maze)
            self.cols = len(self.maze[0]) if self.rows > 0 else 0
    
        def get_maze(self):
            return copy.copy(self.maze)
    
        def __str__(self):
            as_string = u''
            for row in self.maze:
                as_string += u''.join([str(s)[-1] for s in row]) + "\n"
            return as_string
    
    
    maze = Maze()
    maze.load_from_file(sys.argv[1])
    maze.solve(show_path=True)
    print str(maze)
    

    【讨论】:

    • LMAO。没那么严重......这只是一个简单的深度优先搜索哈哈。你需要放松一下,也许可以睡一觉。
    • @MaxEisenhardt 我刚刚复制粘贴了一个之前为解决类似任务而实施的解决方案。
    • 同样,它是二维矩阵上的简单 DFS。它不需要超过 200 行代码 LMAO!哥们,放松一下,好好睡一觉。你太努力了!
    • @MaxEisenhardt 老兄,我毫不怀疑可能会有更好的解决方案。我的编写有两个要求——满足编码挑战平台 I/O 的期望,并且在低级别上易于阅读和理解。顺便说一句,向我们展示您的解决方案,您将使这个社区变得更好。 :)
    【解决方案3】:
    import os
    
    class Maze_Crawler:
        def __init__(self):
            self.maze = []
            
        def load_maze(self, path):
            
            rows = []
            
            with open(path, 'r') as f:
                rows = f.readlines()
            
            for i in range(len(rows)):
                self.maze.append([])
            
                for j in range(len(rows[i])-1):
                    self.maze[i].append(rows[i][j])
            
            return self.maze
        
        def get_start_coor(self):
            for i in range(len(self.maze)):
                for j in range(len(self.maze[i])):
                    if self.maze[i][j] == 'S':
                        return i, j
            return -1, -1
        
        def solve_maze(self, coor):
            x, y = coor
            
            if self.maze[x][y] == '#' or self.maze[x][y] == 'X':
                return False
            
            if self.maze[x][y] == 'E':
                return True
            
            
            if self.maze[x][y] != 'S':
                self.maze[x][y] = 'X'
            
            if self.solve_maze((x+1, y)):
                if self.maze[x][y] != 'S':
                    self.maze[x][y] = 'v'
                
            elif self.solve_maze((x-1, y)):
                if self.maze[x][y] != 'S':
                    self.maze[x][y] = '^'
                
            elif self.solve_maze((x, y+1)):
                if self.maze[x][y] != 'S':
                    self.maze[x][y] = '>'
                
            elif self.solve_maze((x, y-1)):
                if self.maze[x][y] != 'S':
                    self.maze[x][y] = '<'
            else:
                return False
            
            return True
          
        def show_solution(self):
            for i in range(len(self.maze)):
                r = ''
                for j in range(len(self.maze[i])):
                    if self.maze[i][j] == 'X':
                        r +=  ' '
                    else:
                        r += self.maze[i][j]
                print(r)
    

    【讨论】:

      【解决方案4】:

      Maze solving with python 显示我的答案。但是,如果您想自己编写代码,步骤是。

       1. Start at the entrance.  
       2. Call the function solve(x,y) with the entrance co-ordinates  
       3. in solve, return false if the input point has already been handled or is a wall.  
       4. Mark the current point as handled (tag = 'o')  
       5. go to the right and call solve on that point. If it returns true, set tag to '>'  
       6 elif do the same for left and '<'  
       7 elif do the same for up and '^'  
       8 elif do the same for down and 'v'  
       9 else this is a false path, set tag = ' ' 
      10 set the current maze point to tag
      11 return (tag != ' ')
      

      或者将步骤 9 排除在外,进行步骤 11

      return(tag != 'o')
      

      然后在迷宫中搜索并将每个 'o' 替换为 ' '

      您可以以两种方式显示迷宫,以便它显示您如何尝试解决它以及最终答案。这已被用作 Solaris 屏幕保护程序,潜在路径以一种颜色显示,实际路径以不同颜色显示,以便您可以看到它正在尝试然后成功。

      【讨论】:

        【解决方案5】:

        递归实际上是一个简单的想法:要解决一个问题,您将问题缩小一步,然后解决缩小的问题。这种情况一直持续到你遇到一个你知道如何完全解决的“基本问题”。您返回基本解决方案,然后添加到每个步骤返回的解决方案,直到获得完整解决方案。

        所以要求解 n!,我们记住 n 并求解 (n-1)!。基本情况是 1!,为此我们返回 1;然后在每个返回步骤中,我们乘以记住的数字(2 * 1!是 2,3 * 2!是 6,4 * 3!是 24,5 * 4!是 120)直到我们乘以 n 并得到完整的解决方案.这实际上是一种相当苍白和贫血的递归。每一步只有一个可能的决定。被称为“尾递归”,这很容易翻转并转换为迭代解决方案(从 1 开始并乘以每个数字直到 n)。

        一种更有趣的递归是将问题分成两半,解决每一半,然后组合两个半解决方案;例如,快速排序通过选择一个项目对列表进行排序,将列表分为“小于项目的所有内容”和“大于项目的所有内容”,对每一半进行快速排序,然后返回快速排序(较小)+项目+快速排序(较大)。基本情况是“当我的列表只有一项时,它是排序的”。

        对于迷宫,我们将以四种方式拆分问题 - 如果我从当前位置向右、向左、向上和向下移动,所有可能的解决方案 - 具有只有一个递归搜索才能真正找到的特殊功能一个解法。基本情况是“我站在 E 上”,失败是“我在墙上”或“我在一个我已经访问过的空间”。


        编辑:出于兴趣,这里是一个面向对象的解决方案(兼容 Python 2.x 和 3.x):

        from collections import namedtuple
        
        Dir = namedtuple("Dir", ["char", "dy", "dx"])
        
        class Maze:
            START = "S"
            END   = "E"
            WALL  = "#"
            PATH  = " "
            OPEN  = {PATH, END}  # map locations you can move to (not WALL or already explored)
        
            RIGHT = Dir(">",  0,  1)
            DOWN  = Dir("v",  1,  0)
            LEFT  = Dir("<",  0, -1)
            UP    = Dir("^", -1,  0)
            DIRS  = [RIGHT, DOWN, LEFT, UP]
        
            @classmethod
            def load_maze(cls, fname):
                with open(fname) as inf:
                    lines = (line.rstrip("\r\n") for line in inf)
                    maze  = [list(line) for line in lines]
                return cls(maze)
        
            def __init__(self, maze):
                self.maze = maze
        
            def __str__(self):
                return "\n".join(''.join(line) for line in self.maze)
        
            def find_start(self):
                for y,line in enumerate(self.maze):
                    try:
                        x = line.index("S")
                        return y, x
                    except ValueError:
                        pass
        
                # not found!
                raise ValueError("Start location not found")
        
            def solve(self, y, x):
                if self.maze[y][x] == Maze.END:
                    # base case - endpoint has been found
                    return True
                else:
                    # search recursively in each direction from here
                    for dir in Maze.DIRS:
                        ny, nx = y + dir.dy, x + dir.dx
                        if self.maze[ny][nx] in Maze.OPEN:  # can I go this way?
                            if self.maze[y][x] != Maze.START: # don't overwrite Maze.START
                                self.maze[y][x] = dir.char  # mark direction chosen
                            if self.solve(ny, nx):          # recurse...
                                return True                 # solution found!
        
                    # no solution found from this location
                    if self.maze[y][x] != Maze.START:       # don't overwrite Maze.START
                        self.maze[y][x] = Maze.PATH         # clear failed search from map
                    return False
        
        def main():
            maze = Maze.load_maze("somemaze.txt")
        
            print("Maze loaded:")
            print(maze)
        
            try:
                sy, sx = maze.find_start()
                print("solving...")
                if maze.solve(sy, sx):
                    print(maze)
                else:
                    print("    no solution found")
            except ValueError:
                print("No start point found.")
        
        if __name__=="__main__":
            main()
        

        当运行产生:

        Maze loaded:
            ####################################
            #S#  ##  ######## # #      #     # #
            # #   #             # #        #   #
            #   # ##### ## ###### # #######  # #
            ### # ##    ##      # # #     #### #
            #   #    #  #######   #   ###    #E#
            ####################################
        solving...
            ####################################
            #S#  ##  ######## # #>>>>>v#  >>v# #
            #v#>>v#    >>>v     #^#   >>>>^#>>v#
            #>>^#v#####^##v######^# #######  #v#
            ### #v##>>>^##>>>>>v#^# #     ####v#
            #   #>>>^#  #######>>^#   ###    #E#
            ####################################
        

        请注意,给定的赋值有一些非 Pythonic 元素:

        • 它要求camelCase 函数名而不是underscore_separated
        • 它建议使用全局变量而不是显式传递数据
        • 它要求find_start 在失败时返回标志值,而不是引发异常

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-12-11
          • 1970-01-01
          • 2014-05-08
          • 1970-01-01
          • 2019-03-18
          • 2016-05-14
          • 2014-05-07
          • 1970-01-01
          相关资源
          最近更新 更多