我将如何在此处实施回溯?

问题描述

我一直努力为我的数独求解器找到正确的逻辑。到目前为止,我已经创建了一个函数获取 (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,y):
                            row[x] = i
                        elif i == 9 and possible(i,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,2],[0,4,1,0],3,2,[4,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

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