1. 程式人生 > >python資料結構學習筆記-2017-01-08-01-N皇后問題、迷宮問題和跳馬問題的遞迴解決

python資料結構學習筆記-2017-01-08-01-N皇后問題、迷宮問題和跳馬問題的遞迴解決

        N皇后問題

        棋盤ADT

#-*-coding: utf-8-*-

# 二維陣列實現棋盤ADT

from myarray2d import Array2D

class Board(object):
    def __init__(self, n):
        self._board = Array2D(n, n)
        self._size = n
    
    def size(self):
        return self._size

    def numQueens(self):
        count = 0
        for row in range(self.size()):
            for col in range(self.size()):
                if self._board[row, col] == 1:
                    count += 1
        return count

    def unguarded(self, row, col):        
        if self._board[row, col]:
            return False
        else:
            directions = [(0, 1), (0, -1), (-1, 0), (1, 0), (1, 1), (-1, -1), (1, -1), (-1, 1)]
            for direction in directions:
                r = row
                c = col
                while 0 <= r < self.size() and 0 <= c < self.size():
                    if self._board[r, c] == 1:
                        return False
                    else:
                        r += direction[0]
                        c += direction[1]
            return True

    def placeQueen(self, row, col):
        assert self.unguarded(row, col), "Cannot place a queen here."
        self._board[row, col] = 1

    def removeQueen(self, row, col):
        self._board[row, col] = 0

    def reset(self):
         for row in range(self.size()):
            for col in range(self.size()):
                if self._board[row, col] == 1:
                    self.removeQueen(row, col)

    def draw(self):
        for row in range(self.size()):
            string = ''
            for col in range(self.size()):
                if self._board[row, col]:
                    string += '@ '
                else:
                    string += '. '
            print string
         遞迴解決N皇后問題,返回解的個數,修改一下,也可以返回全部解。
#-*-coding: utf-8-*-

# N皇后問題

from board import Board

# 該函式只是驗證N皇后問題解的存在性
def solveNQueens(board, col):
    if board.numQueens() == board.size(): # 確定所有皇后是否都放置好了
        return True
    else:
        # 在這一列找出正確的位置放置皇后        
        for row in range(board.size()):
            if board.unguarded(row, col):
                board.placeQueen(row, col)
                if solveNQueens(board, col+1):
                    return True
                else:
                    board.removeQueen(row, col)
        return False # 沒有找到正確位置,則會回溯到上一列的皇后放置上來。

# 返回的count是solveNQueensCounts(board, col),其中的col是最開始的0,如果使用區域性變數有些麻煩,用全域性變數就相對好處理一些。
count = 0
def solveNQueensCounts(board, col):
    for row in range(board.size()):
        if board.unguarded(row, col):
            board.placeQueen(row, col)
            if board.numQueens() == board.size(): # 確定找到一個解了
                # board.draw()
                global count
                count += 1
                board.removeQueen(row, col) # 確定一個解後,需要將最後一個皇后拿掉
            else:
                solveNQueensCounts(board, col+1)
                board.removeQueen(row, col) # 無論在col+1列放置皇后是否成功,都必須將原來在col列放置的皇后拿掉,再嘗試在該列的下一個位置。因為col+1列沒有放置成功,顯然放置在col列的皇后要拿掉。如果在col+1列放置成功,並找到了在前面col列的皇后都不動的情況下的所有解之後,要尋找接下來的解,顯然也要移動col列上的皇后。
    return count

if __name__ == "__main__":
    n = int(raw_input("Please enter the size of board: "))
    board = Board(n)
    # solveNQueens(board, 0)
    # board.draw()
    print solveNQueensCounts(board, 0)

        迷宮問題

        迷宮ADT

#-*-coding: utf-8-*-

# 迷宮ADT

from myarray2d import Array2D
# from lliststack import Stack

class Maze(object):
    MAZE_WALL = "*" # 牆
    PATH_TOKEN = "x" # 表示走過的路徑
    TRIED_TOKEN = "o" # 死路
    PathFound = False

    def __init__(self, numRows, numCols):
        self._mazeCells = Array2D(numRows, numCols)
        self._startCell = None
        self._exitCell = None

    def numRows(self):
        return self._mazeCells.numRows()

    def numCols(self):
        return self._mazeCells.numCols()

    def setWall(self, row, col):
        assert 0 <= row < self.numRows() and 0 <= col < self.numCols(), "Cell index out of range."
        self._mazeCells[row, col] = Maze.MAZE_WALL

    def setStart(self, row, col):
        assert 0 <= row < self.numRows() and 0 <= col < self.numCols(), "Cell index out of range."
        self._startCell = _CellPosition(row, col)

    def setExit(self, row, col):
        assert 0 <= row < self.numRows() and 0 <= col < self.numCols(), "Cell index out of range."
        self._exitCell = _CellPosition(row, col)

    def getStart(self):
        return self._startCell.row, self._startCell.col

    def getExit(self):
        return self._exitCell.row, self._exitCell.col

    def findPath(self, row, col):
        self._markPath(row, col) # 首先對當前位置進行標記'x'
        # print row, col
        if self._exitFound(row, col): # 確認當前位置是否是終點,是就修改類屬性Maze.PathFound
            Maze.PathFound = True
        else:
            chioce = 0 # 先確定在這一位置上是否有路可走
            dirctions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
            for dirction in dirctions:
                r = row + dirction[0]
                c = col + dirction[1]
                if self._validMove(r, c):
                    chioce += 1
            if chioce == 0: # 若無路可走則標記'o'
                self._markTried(row, col)
            else:
                for dirction in dirctions:
                    r = row + dirction[0]
                    c = col + dirction[1]
                    if Maze.PathFound: # 一旦確認已到達終點,停止遞迴呼叫,進入遞迴返回
                        break
                    if self._validMove(r, c):
                        self.findPath(r, c)
        if Maze.PathFound: #    確認是否找到到達終點的路徑
            return True
        else:
            return False

    # 刪除所有標記,即"x"和"o"。
    def reset(self):
        for row in range(self.numRows()):
            for col in range(self.numCols()):
                if self._mazeCells[row, col] in 'ox':
                    self._mazeCells[row, col] = None

    def draw(self):
        for row in range(self.numRows()):
            str = ''
            for col in range(self.numCols()):
                if self._mazeCells[row, col] != None:
                    str += self._mazeCells[row, col]
                else:
                    str += '.'
            print str

    # 是否能移動到該位置
    def _validMove(self, row, col):
        return 0 <= row < self.numRows() and 0 <= col < self.numCols() and self._mazeCells[row, col] is None

    # 判斷當前點是否為終點
    def _exitFound(self, row, col):
        return row == self._exitCell.row and col == self._exitCell.col

    # 將該位置設定為死路
    def _markTried(self, row, col):
        self._mazeCells[row, col] = Maze.TRIED_TOKEN

    # 標記走過的路
    def _markPath(self, row, col):
        self._mazeCells[row, col] = Maze.PATH_TOKEN

# 儲存類
class _CellPosition(object):
    def __init__(self, row, col):
        self.row = row
        self.col = col
        遞迴解決迷宮問題
#-*-coding: utf-8-*-

# 從檔案中建立迷宮,並解決迷宮

from recmaze import Maze

# 建立迷宮
def buildMaze(filename):
    with open(filename, 'r') as infile:
        nrows, ncols = readValuePair(infile) # 迷宮大小
        maze = Maze(nrows, ncols) # 建立迷宮,並初始化

        row, col = readValuePair(infile)
        maze.setStart(row, col) # 根據給定座標設定起始點
        row, col = readValuePair(infile)
        maze.setExit(row, col)

        for row in range(nrows):
            line = infile.readline()
            for col in range(len(line)):
                if line[col] == "*":
                    maze.setWall(row, col)
        infile.close()
    return maze

# 輔助方法,從給定檔案中讀取整數對值
def readValuePair(infile):
    line = infile.readline()
    (valA, valB) = tuple(line.split())
    return int(valA), int(valB)

def main():
    maze = buildMaze("mazefile.txt")
    if maze.findPath(maze.getStart()[0], maze.getStart()[1]):
        print "Path found ..."
        maze.draw()
    else:
        print "Path not found ..."

if __name__ == "__main__":
    main()
         

        跳馬問題

        棋盤ADT

#-*-coding: utf-8-*-

# 跳馬問題國際象棋棋盤ADT

from myarray2d import Array2D

class ChessBoard(object):
    PATH_FOUND = False
    COUNT = 0

    def __init__(self, n):
        self._board = Array2D(n, n)
        self._size = n
        self._start = None
        
    def size(self):
        return self._size
    
    def setStart(self, row, col):
        assert 0 <= row < self._size and 0 <= col < self._size, "Out of range."
        self._start = _CellPosition(row, col)
        
    def _validMove(self, row, col):
        return 0 <= row < self._size and 0 <= col < self._size and self._board[row, col] == None
       
    # 尋找路徑    
    def findPath(self, row, col):
        # print row, col
        self._board[row, col] = ChessBoard.COUNT
        ChessBoard.COUNT += 1
        if ChessBoard.COUNT == self._size ** 2: # 通過步數來判斷棋盤是否已經遍歷完
            ChessBoard.PATH_FOUND = True
        else:
            choice = 0
            directions = [(1, 2), (2, 1), (-1, -2), (-2, -1), (1, -2), (-1, 2), (-2, 1), (2, -1)] # 方向
            for direction in directions:
                r = row + direction[0]
                c = col + direction[1]
                if self._validMove(r, c):
                    choice += 1
            if choice:
                for dirction in directions:
                    r = row + dirction[0]
                    c = col + dirction[1]
                    if self._validMove(r, c):
                        self.findPath(r, c)
            else:
                self._board[row, col] = None
                ChessBoard.COUNT -= 1
                return False
        if ChessBoard.PATH_FOUND:
            print row, col
            return True
        else:
            self._board[row, col] = None # 注意如果一個位置有路可走,但經試驗都是死路的話,必須將這個位置復原!
            
class _CellPosition(object):
    def __init__(self, row, col):
        self.row = row
        self.col = col    
       遞迴解決跳馬問題
#-*-coding: utf-8-*-

from recchessboard import ChessBoard

def knightTour(board, row, col):
    if isinstance(board, ChessBoard):
        board.setStart(row, col)
        board.findPath(row, col)

if __name__ == "__main__":
    board = ChessBoard(5)
    row = int(raw_input("Please enter a positive integer less than 5: "))
    col = int(raw_input("Please enter a positive integer less than 5: "))
    knightTour(board, row, col)
    print board.COUNT


        以上的程式碼還是有不少可優化的空間。