【问题标题】:N-queens backtrack not working for n=9 and higherN-queens 回溯不适用于 n=9 及更高版本
【发布时间】:2021-11-07 06:49:39
【问题描述】:

我一直在网上练习标准的回溯和回溯示例,并且遇到了 N-queens 问题(在 LeetCode 设置中)。 经过大量修改后,我设法应用递归来检索给定板尺寸 n 的任何解决方案,而不是全部解决方案。

但是,我的算法在 n=8 时有效,打印出有效的电路板配置,但当 n=9 或等于我尝试的几个更高的数字时无效。无效意味着一些棋盘行充满了点并且没有由“Q”皇后填充,但回溯未能捕捉到这一点,可能是由于递归错误。

例如,对于 n=9,这是输出:

testing backtrack
['Q........', '..Q......', '....Q....', '.Q.......', '...Q.....', '........Q', '.........', '.........', '.........']

testing backtrack
['..Q......', 'Q........', '...Q.....', '.Q.......', '....Q....', '........Q', '.........', '.........', '.........']

testing backtrack
['.Q.......', '...Q.....', 'Q........', '..Q......', '....Q....', '........Q', '.........', '.........', '.........']

testing backtrack
['.Q.......', '...Q.....', '.....Q...', 'Q........', '..Q......', '....Q....', '......Q..', '.........', '.........']

testing backtrack
['.Q.......', '....Q....', '......Q..', '...Q.....', 'Q........', '..Q......', '.....Q...', '.........', '.........']

testing backtrack
['.Q.......', '...Q.....', '.....Q...', '.......Q.', '..Q......', 'Q........', '......Q..', '....Q....', '.........']

testing backtrack
['.Q.......', '...Q.....', '.....Q...', '..Q......', '....Q....', '.........', 'Q........', '.........', '......Q..']

testing backtrack
['.Q.......', '...Q.....', '......Q..', '..Q......', '.......Q.', '.....Q...', '.........', 'Q........', '....Q....']

testing backtrack
['.Q.......', '...Q.....', '.....Q...', '..Q......', '........Q', '.........', '....Q....', '.......Q.', 'Q........']

您可以看到,在所有情况下,棋盘中至少有一排似乎没有皇后。

谁能告诉我回溯可能在下面的算法中失败的地方? 提前谢谢!

    class Solution:
      def __init__(self) -> None:
        self.board =  ["."*n] * n
        self.n_queens = n    
        self.queenPos = []
    
      def solveNQueens(self, n: int) -> list[list[str]]:
    
        def changeLetter(letter, i,j):
          # change letter in board
          s = list(self.board[i])
          s[j] = letter
          self.board[i] = "".join(s)
          if letter == "Q":
            self.queenPos.append([i,j])
          else:
            self.queenPos.pop()
    
        def boardOk(k,l):
          # print(self.queenPos)
          def check_attack(piece_1, piece_2):
            # check if they are in the same row
            if piece_1[0] == piece_2[0]:
                return True
            # check if they are in the same column
            elif piece_1[1] == piece_2[1]:
                return True
            # check if they are in the same diagonal
            elif abs(piece_1[0] - piece_2[0]) == abs(piece_1[1] - piece_2[1]):
                return True
            else:
                # print("queens are not attacking in diagonal")
                return False
          
          if len(self.queenPos)>0:
            # print(self.queenPos)
            for pos in self.queenPos:
              if check_attack([k,l], pos):
                return False
    
          return True
    
        def backtrack(numQueens, i, j):
          
          if boardOk(i,j):
              changeLetter("Q", i,j)
              self.n_queens-=1
          else:
            return
          
          if self.n_queens<=0:
            return
                    
          for k in range(n):
            for l in range(n):
              backtrack(self.n_queens, k, l)
          
        i=0
        while self.n_queens!=0:
          print(f"\ntesting backtrack")
          # print(f"\ti={i}")
          self.board =  ["."*n] * n
          self.n_queens = n
          self.queenPos = []
          backtrack(n, i, 0) # this works for all cases except 9 instead of backtrack(n,0,i) which doesn't except for 4
          print(self.board)
          if i+1<n :
            i+=1 
          else:
            break  
    
        return
    
    if __name__=="__main__":
        n=9
        sol = Solution()
        sol.solveNQueens(n)

【问题讨论】:

  • 抱歉。在这种情况下无法工作意味着返回的棋盘配置无效,更具体地说,由于某种原因,棋盘没有被全部 n 个可用皇后填充,而是有些行未填充并且算法无法管理抓住那个条件。这在执行期间的输出“测试回溯”行中可见。
  • 其实我会把它们都包含在描述中。
  • 虽然这不一定是错误的直接功能原因,但使代码极难调试的一件事是变量的使用(范围和命名)。该类不将 'n' 作为输入参数,而是复制一个全局 (self.n_queens = n) 来定义实例变量,并将一个隐藏名称 n 传递给求解函数。然后solver方法在外循环中修改self.n_queens,将n作为参数传给backtrack()作为numQueens,未使用;同时回溯修改self.n_queens,同时也使用n。目前还不清楚这个程序是否会停止。

标签: python recursion backtracking n-queens


【解决方案1】:

好吧,忘记我的 cmets。我以为对角线测试是错误的,我只是以为你错误地应用了不同的想法,但你应用的想法是正确的。

您的实际问题是您没有正确回溯:您只需为每个皇后尝试第一个位置,然后仅重试放置第一个。 backtrack 需要实际回溯,例如删除它的变化:


        def backtrack(numQueens, i, j):
          
          if boardOk(i,j):
              changeLetter("Q", i,j)
              self.n_queens-=1
          else:
            return False # This is failing
          
          if self.n_queens == 0:
            return True # We found a solution
                    
          for k in range(n):
            for l in range(n):
              if backtrack(self.n_queens, k, l):
                  return True # We found a solution
          changeLetter(".", i, j) # Remove the Queen we tried from the board.
          self.n_queens += 1
          return False # None of the tries for other Queens works

解决方案中的while循环可以是for循环:

        self.board =  ["." * n] * n # You don't need to reset these each loop. backtrack cleans up behind it.
        self.n_queens = n
        self.queenPos = []
        for i in range(n):
          if backtrack(n, i, 0):
            print(self.board)
            return self.board
        else:
            raise ValueError("We did not find a solution")
    

【讨论】:

  • 非常感谢!这就是我一直在寻找的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 2020-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多