我将如何在这里实施回溯?

How would I implement backtracking here?

我一直在努力为我的数独求解器找到正确的逻辑。到目前为止,我已经创建了一个函数来获取 (x,y) 坐标和数字并检查它是否合法。

虽然我不明白如何遍历网格,用合法数字替换每个 0,然后返回并修复使求解不正确的数字。

例如,有时我会在网格上留下 0's,因为有些数字无法再播放。我对其进行了更深入的研究,并意识到之前在同一行中播放的数字 是合法的 但毁了 谜题。相反,如果这些数字再高一点,难题就会解决。

我知道 回溯 是我的朋友,但是我不知道如何实现它。到目前为止,这就是我所拥有的。

solve.py

import numpy as np
import time
class sodoku:
    def __init__(self,grid,boxRange):
        self.grid = grid
        self.boxRange = boxRange
    def show(self):
        for row in self.grid:
            print(row)
    def solve(self):
        def possible(num,x,y):
            def box(x,y):                       
                board = np.array(self.grid)
                result = {}
                size = 3
                for i in range(len(board) // size):
                    for j in range(len(board) // size):
                        values = board[j * size:(j + 1) * size, i * size:(i + 1) * size]
                        result[i * size + j + 1] = values.flatten()
                if y <= 2 and x <= 2:
                    squareBox = result[1]
                if (y <= 5 and y > 2) and x <= 2:
                    squareBox = result[2]
                if (y <= 8 and y > 5) and x <= 2:
                    squareBox = result[3]

                if (y <= 2 ) and (x <= 5 and x > 2):
                    squareBox = result[4]
                if (y <= 5 and y > 2)and (x <= 5 and x > 2):
                    squareBox = result[5]
                if (y <= 8 and y > 5)and (x <= 5 and x > 2):
                    squareBox = result[6]
            
                if (y <= 2) and (x <= 8 and x > 5):
                    squareBox = result[7]
                if (y <= 5 and y > 2)and (x <= 8 and x > 5):
                    squareBox = result[8]
                if (y <= 8 and y > 5)and (x <= 8 and x > 5):
                    squareBox = result[9]
                return squareBox
            row = self.grid[y]
            column= [r[x] for r in self.grid]
            square = box(x,y)
            if (num not in row) and (num not in column) and (num not in square):
                return True
            else:
                return False
        y = 0
        for row in self.grid:
            x = 0
            for number in row:
                if number == 0:
                    for i in range(1,10):
                        if possible(i,x,y):
                            row[x] = i
                        elif i == 9 and possible(i,x,y) == False: pass
                        #what would I do here now

                x += 1
            y += 1
      
boxRange = "3x3"           
bxd = []
with open('board.txt', 'r') as f:
    for line in f:
        line = line.strip()
        line = line.split(' ')
        bLine = [int(x) for x in line]
        bxd.append(bLine)
# brd = [[3,0,0,2],[0,4,1,0],[0,3,2,0],[4,0,0,1]]
brd = sodoku(bxd,boxRange)
brd.show()
brd.solve()
print('-----Solved------')
brd.show()

board.txt

5 3 0 0 7 0 1 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9

递归伪代码回溯数独求解器:

#solve will return a solved board, or None if it fails
def solve(board):
    #case 1: board is solved
    if board.is_solved: #simple check for leftover empty spaces
        return board #board is solved. unzip the call stack

    pos = board.next_empty_space()
    valid = [i for i in range(1,10) if board.is_valid(pos, i)]

    #case 2: not solved and no more valid moves
    if not valid:
        return None #no valid moves left

    new_board = copy(board) #don't step on the original data in case this isn't the solution
    for value in valid:
        new_board[pos] = value
        result = solve(new_board)

        #case 3: one of the valid moves led to a valid solution
        if result is not None: #we found a fully solved board
            return result #here's where we unzip the call stack

    #case 4: none of the valid moves led to a valid solution
    return None #none of the valid moves panned out

基本上,您将棋盘上的每个空 space 视为树中的一个分支,并且您从每个分支的子分支插入一个当前在树中该点有效的新数字。如果您到达一个分支的末尾,并且没有更多有效的移动离开(子分支),您要么已成功填写所有空白 space,要么其中一个数字是错误的。当 None 返回,并且执行返回到调用者(在调用堆栈中向上一个帧)时,for 循环中遍历有效移动的本地位置就是“记住”你所在的位置在,以及下一个可能的有效移动应该是什么。它基本上是一种用于正确棋盘状态的深度优先树搜索算法。