2014-03-28 58 views
1

因此,我有一個任務,要求我使用遞歸解決迷宮問題。我將發佈作業指南,以便您可以看到我在說什麼。教授沒有多少解釋遞歸,他給了我遞歸的例子,我會發布,但我希望有人能夠給我一個更深入的遞歸解釋,以及我將如何應用它來解決一個迷宮。我沒有要求任何人寫代碼,我只是希望有一些解釋能讓我走上正確的道路。感謝任何回答的人。在Python中使用遞歸解決迷宮問題

這裏有例子,我有:

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座標。你可能會認爲在迷宮中至多存在一個這樣的人物。如果在迷宮返回-1中找不到'S'作爲x和y座標。

在繼續之前,在多個位置(包括無位置)用'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]!= ' ') 
+1

可能會有所幫助:http://stackoverflow.com/questions/22700130/maze-solving-with-python – sshashank124

+0

謝謝!我一直在尋找例子,但是我沒有找到對我真正有用的東西!這一個似乎非常有幫助。 – MarissaLeigh

+0

沒問題。如果您遇到任何其他問題,只需使用新查詢更新問題 – sshashank124

回答

1

遞歸實際上是一個簡單的想法:解決一個問題,你一步收縮的問題,然後解決問題減少。這個過程會持續到你遇到一個你知道如何完全解決的「基本問題」。您返回基本解決方案,然後添加到每個步驟返回的解決方案,直到獲得完整解決方案。

所以要解決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# 
    #################################### 

注意給出的分配有幾個unPythonic元素:

  • 它要求camelCase函數的名稱,而不是underscore_separated
  • 它建議使用全局變量而不是明確傳遞數據
  • 它要求find_start返回故障標記值,而不是拋出一個異常
1

(約會我自己,我確實是在COBOL這個問題,在高中。)

你能想到解決的迷宮如同採取措施。

當您採取措施時,每次都應用相同的規則。由於每次都應用相同的規則,因此每個步驟都可以使用完全相同的說明。當你邁出一步時,你只需再次調用相同的例程,改變參數來指示新的步驟。這是遞歸。你一次一步地解決問題。

注意:某些遞歸解決方案將問題分解成一半,解決了每個半個獨立於另一個的問題,這兩個解決方案實際上是獨立的。它在這裏不起作用,因爲每個步驟(解決方案)都依賴於前面的步驟。

如果你遇到死路一條,你退出死路,直到找到一個仍然存在可行方格的步驟。

提示:您沒有標記的方式出口的正確路徑,因爲你不知道你現在正在採取步驟是通向出口的一部分。當您知道每一步確實是路徑的一部分時,您在返回的路上標記路徑。你可以做到這一點,因爲每一步都會記住下一步之前的哪個方塊。

相反,你在每一個你試過的方塊上寫上一個標記,只說:我一直在這裏,不需要再次檢查這個。在打印解決方案之前將其清理乾淨。

0

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屏幕保護程序,潛在路徑以一種顏色顯示,實際路徑以不同顏色顯示,以便您可以看到它嘗試併成功。

3

這裏是我的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)