【问题标题】:Python sudoku backtrackingPython数独回溯
【发布时间】:2021-01-02 11:29:14
【问题描述】:

我正在通过一个简单的示例(数独)尝试回溯算法。我首先尝试了另一种方法,取消了更多可能性,但在遇到同样的错误后,我切换到了更简单的解决方案。

  1. 寻找第一个未解决的点
  2. 填写 1 到 9 之间的每个数字,如果有效则回溯新字段

当我运行它并输出无效字段时,我可以看到当算法退出递归调用时,该递归调用中的位置仍然是 9(因此算法找不到任何东西点)

例如前两行看起来像这样(它试图解决一个空字段):

[1, 2, 3, 4, 6, 9, 9, 9, 9]

[9, 9, 9, 9, 9, 9, 9, 0, 0]

我认为这是一个参考错误并插入 [e for e in field] 在回溯调用中,以便旧字段不会被更改,尽管这似乎没有帮助。

这是我的代码:


    for i in range(9):
        a = [field[i][j] for j in range(9) if field[i][j] != 0]
        if len(a) != len(set(a)):
            return False

    for i in range(9):
        a = [field[j][i] for j in range(9) if field[j][i] != 0]
        if len(a) != len(set(a)):
            return False

    for x in range(3):
        for y in range(3):
            a = []
            for addX in range(3):
                for addY in range(3):
                    spot = field[x * 3 + addX][y * 3 + addY]
                    if spot != 0:
                        a.append(spot)
            if len(a) != len(set(a)):
                return False

    return True

def findEmpty(field):

    for i in range(9):
        for j in range(9):
            if field[i][j] == 0:
                return i, j


def backtracking(field):

    find = findEmpty(field)
    if not find:
        return True, field
    else:
        x, y = find
 
    for i in range(1, 10):
        print(f"Trying {i} at {x} {y}")
        field[x][y] = i
        if isValid(field):
            s = backtracking([e for e in field])
            if s[0]:
                return s
        else:
            print("Not valid")
            for row in field:
                print(row)

    return False, None


field = [[0, 0, 0, 0, 1, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0],
         [0, 0, 0, 0, 0, 0, 0, 0, 0]]

solution = backtracking(field)
if solution[0]:
    print("There was a solution. The field is:")
    for row in solution[1]:
        print(row)
else:
    print("No solution was found")

【问题讨论】:

    标签: python backtracking sudoku


    【解决方案1】:

    好的,所以根据我在日志中看到的情况,当代码到达 9 并且仍然没有得到答案时,它会回溯,但将值保持在 9。

    所以发生的情况是,每次程序回溯时,它都会将值保留为 9,然后转到上一个值,这也可能会转到 9,这是无效的,因为我们回溯的值已经是 9。正如您在示例中看到的那样,这会导致程序直接回溯到开始并使大多数插槽为 9 的循环。

    所以解决方案是在 backtrack() 中添加几行,如下所示。简而言之,额外的 2 行检查无效答案是否为 9,如果是,则将其重置为 0 并回溯到之前的值,直到得到有效答案。

    def backtracking(field):
        find = findEmpty(field)
        if not find:
            return True, field
        else:
            x, y = find
    
        for i in range(1, 10):
            print(f"Trying {i} at {x} {y}")
            field[x][y] = i
            if isValid(field):
                s = backtracking(field)
                if s[0]:
                    return s
            else:
                print("Not valid")
                if field[x][y] == 9:
                    field[x][y] = 0
                for row in field:
                    print(row)
    
        return False, None
    

    它给出的解决方案:

    [2, 3, 4, 5, 1, 6, 7, 8, 9]
    [1, 5, 6, 7, 8, 9, 2, 3, 4]
    [7, 8, 9, 2, 3, 4, 1, 5, 6]
    [3, 1, 2, 4, 5, 7, 6, 9, 8]
    [4, 6, 5, 1, 9, 8, 3, 2, 7]
    [8, 9, 7, 3, 6, 2, 4, 1, 5]
    [5, 2, 8, 6, 4, 1, 9, 7, 3]
    [6, 7, 3, 9, 2, 5, 8, 4, 1]
    [9, 4, 1, 8, 7, 3, 5, 6, 2]
    

    【讨论】:

    • 没错。尽管一种更简单的方法(我也发现)是在原始代码的 for 循环之后添加 field[x][y] = 0。我的问题更多是为什么会发生这种情况,因为我的代码应该可以工作。
    • 哦,也可以。当你进行回溯时,你几乎是在告诉程序只是让它飞起来,因为它看不到前方,所以这样的事情经常发生。
    【解决方案2】:

    我做了一些研究,显然这确实是一个参考错误。对我来说,导入 python 复制库并分配每个新字段说 f = copy.deepcopy(field) 解决了这个问题(这也适用于复杂的示例)。

    【讨论】:

      【解决方案3】:
      class Solution:
          def solveSudoku(self, board: List[List[str]]) -> None:
              """
              Do not return anything, modify board in-place instead.
              """
              n=len(board)
              # create state variables,keep track of rows, cols and boxes
              rows=[{} for _ in range(n)]
              cols=[{} for _ in range(n)]
              boxes=[{} for _ in range(n)]
             # get the initial state of the grid
              for r in range(n):
                  for c in range(n):
                      if board[r][c]!='.':
                          val=board[r][c]
                          box_id=self.get_box_id(r,c)
                          boxes[box_id][val]=True
                          rows[r][val]=True
                          cols[c][val]=True
              # this backtracking just tells if shoul move to the next cell or not
              self.backtrack(board,boxes,rows,cols,0,0)
              
          def backtrack(self,board,boxes,rows,cols,r,c):
              # base case. If I hit the last row or col, means all digits were correct so far
              if r>=len(board) or c>=len(board[0]):
                  return True
              # situation when cell is empty, fill it with correct value
              if board[r][c]==".":
                  for num in range(1,10):
                      box_id=self.get_box_id(r,c)
                      box=boxes[box_id]
                      row=rows[r]
                      col=cols[c]
                      str_num=str(num)
                      # check rows, cols and boxes make sure str_num is not used before
                      if self.is_valid(box,col,row,str_num):
                          board[r][c]=str_num
                          boxes[box_id][str_num]=True
                          cols[c][str_num]=True
                          rows[r][str_num]=True
                          # if I am the last col and I placed the correct val, move to next row. So first col of the next row
                          if c==len(board)-1:
                              if self.backtrack(board,boxes,rows,cols,r+1,0):
                                  return True
                          # if I am in col between 0-8, move to the col+1, in the same row
                          else:
                              if self.backtrack(board,boxes,rows,cols,r,c+1):
                                  return True
                          # If I got a wrong value, then backtrack. So clear the state that you mutated
                          del box[str_num]
                          del row[str_num]
                          del col[str_num]
                          board[r][c]="."
             # if cell is not empty just call the next backtracking
              else:
                  if c==len(board)-1:
                      if self.backtrack(board,boxes,rows,cols,r+1,0):
                          return True
                  else:
                      if self.backtrack(board,boxes,rows,cols,r,c+1):
                          return True
              return False
                      
          def is_valid(self,box,row,col,num):
              if num in box or num in row or num in col:
                  return False
              else:
                  return True
              
              
          # a helper to get the id of the 3x3 sub grid, given row and column
          def get_box_id(self,r,c):
              row=(r//3)*3
              col=c//3
              return row+col
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多