簡體   English   中英

“自我”如何正確更新原始變量? N-皇后問題中的遞歸/回溯(Python)

[英]How can "self" update original variable correctly? Recursion/Backtracking in the N-queens problem (Python)

這是我解決8皇后問題的python程序。 除了打印已解決的電路板的最后一步之外,一切都在工作。 我使用遞歸/回溯來用皇后填充棋盤,直到找到解決方案。 保存解決方案的板對象是self ,它是對b1的引用,所以我假設b1是我初始化的原始板,將被更新以包含最終解決的板,並將使用printBoard打印解決方案。 但是,由於某種未知原因,當我打印b1它沒有更新並且持有一個失敗的板。

編輯:在solve添加了placeQueen

EMPTY = 0
QUEEN = 1
RESTRICTED = 2

class Board:

    # initializes a 8x8 array
    def __init__ (self):
        self.board = [[EMPTY for x in range(8)] for y in range(8)]

    # pretty prints board
    def printBoard(self):
        for row in self.board:
            print(row)

    # places a queen on a board
    def placeQueen(self, x, y):
        # restricts row
        self.board[y] = [RESTRICTED for i in range(8)]

        # restricts column
        for row in self.board:
            row[x] = RESTRICTED

        # places queen
        self.board[y][x] = QUEEN

        self.fillDiagonal(x, y, 0, 0, -1, -1)   # restricts top left diagonal
        self.fillDiagonal(x, y, 7, 0, 1, -1)    # restructs top right diagonal
        self.fillDiagonal(x, y, 0, 7, -1, 1)    # restricts bottom left diagonal
        self.fillDiagonal(x, y, 7, 7, 1, 1)     # restricts bottom right diagonal

    # restricts a diagonal in a specified direction
    def fillDiagonal(self, x, y, xlim, ylim, xadd, yadd):
        if x != xlim and y != ylim:
            self.board[y + yadd][x + xadd] = RESTRICTED
            self.fillDiagonal(x + xadd, y + yadd, xlim, ylim, xadd, yadd)

    # recursively places queens such that no queen shares a row or
    # column with another queen, or in other words, no queen sits on a
    # restricted square. Should solve by backtracking until solution is found.
    def solve(self, col):

        if col == -1:
            return True

        for i in range(8):
            if self.board[i][col] == EMPTY:
                temp = self.copy()

                self.placeQueen(col, i)
                if self.solve(col - 1):
                    return True

                temp.board[i][col] = RESTRICTED
                self = temp.copy()

        return False

    # deep copies a board onto another board
    def copy(self):
        copy = Board()

        for i in range(8):
            for j in range (8):
                copy.board[j][i] = self.board[j][i]

        return copy


b1 = Board()
b1.solve(7)
b1.printBoard()

我知道我的實際求解器正在工作,因為當我像這樣添加printBoard

if col == -1:
    self.printBoard()
    return True

solve方法中,印刷求解板。 簡而言之,為什么板的self實例不更新b1

我相信您的問題與在解決方法中重新定義自我有關,我什至不確定您為什么要這樣做。

有關更多詳細信息,請參閱此問題: 在方法中將 self 對象替換為相同類型的另一個對象是否安全?

像您正在做的那樣重新分配 self 並不是重新分配“b1”引用。 因此,當您再次引用 b1 並執行 printBoard 時,您引用的對象與解決完成時“self.printBoard()”將引用的對象不同。

我會退后一步,問問自己為什么要替換 self ,這對您有什么好處。 您可能不需要也不應該這樣做。

我不確定這是如何工作的,因為從來沒有調用過placeQueen 因此,我不認為按照建議添加印刷品會呈現成品板(我認為它是空的)。 [注意:最新更新修復了這個]

使用受限方塊的想法是可行的,但這里的實現方式(沒有撤消選項)效率低下; 為每個內部循環復制一個全新的Board對象非常昂貴。 對於所有的麻煩,我們也可以在每次移動時執行迭代沖突檢查,這至少可以節省新堆對象的分配和垃圾收集成本。

至於返回完成的板子結果,使用selfself.board的返回值,失敗時使用None而不是TrueFalse

其他幾點:

  • 由於解決謎題不需要狀態,我們可以(希望)同意復制棋盤效率低下,我不確定允許__init__方法是否__init__ 該類作為封裝結構很好,我們應該在Board類中隱藏諸如EMPTYQUEEN等靜態變量,無論該類是靜態的還是實例化的。
  • 如果您決定保持類有狀態,則printBoard不應產生副作用--override __str__
  • 不要在整個代碼中硬編碼大小文字,例如 8; 這使得類變得僵化、難以維護並且容易出現拼寫錯誤和一對一錯誤。 改用len(self.board)並自由提供參數。
  • fillDiagonal不需要遞歸。 考慮使用列表推導式或 numpy 來簡化此矩陣遍歷邏輯。
  • 根據PEP-8使用snake_case變量名稱和文檔字符串而不是主題標簽注釋。 如果你覺得有必要寫一個評論,比如# restricts column ,可以考慮將相關的塊移動到一個名為restrict_column(...)的函數中並跳過評​​論。

這是實現以下幾點的初始重寫:

class Board:
    EMPTY = 0
    QUEEN = 1
    DIRS = [(x, y) for x in range(-1, 2) for y in range(-1, 2) if x]

    def __init__ (self, size=8):
        self.board = [[Board.EMPTY] * size for _ in range(size)]

    def __str__(self):
        return "\n".join(map(str, self.board))

    def legal_from(self, row, col, dr, dc):
        while row >= 0 and row < len(self.board) and \
              col >= 0 and col < len(self.board[row]):
            if self.board[row][col] != Board.EMPTY:
                return False

            row += dr; col += dc

        return True

    def legal_move(self, row, col):
        return all([self.legal_from(row, col, *d) for d in Board.DIRS])

    def solve(self, row=0):
        if row >= len(self.board): 
            return self

        for col in range(len(self.board[row])):
            if self.legal_move(row, col):
                self.board[row][col] = Board.QUEEN

                if self.solve(row + 1):
                    return self

                self.board[row][col] = Board.EMPTY

if __name__ == "__main__":
    for result in [Board(i).solve() for i in range(9)]:
        print(result, "\n")

輸出:

[1]

None

None

[0, 1, 0, 0]
[0, 0, 0, 1]
[1, 0, 0, 0]
[0, 0, 1, 0]

[1, 0, 0, 0, 0]
[0, 0, 1, 0, 0]
[0, 0, 0, 0, 1]
[0, 1, 0, 0, 0]
[0, 0, 0, 1, 0]

[0, 1, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 1]
[1, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 1, 0]

[1, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 1]
[0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 0]

[1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0]
[0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM