【问题标题】:Backtracking algorithm that finds solutions for sudoku寻找数独解决方案的回溯算法
【发布时间】:2023-05-28 20:07:01
【问题描述】:

我正在研究算法理论及其可能的解决方法。在这种情况下,我在回溯方面遇到了一些问题。 我想写一个填充数独的函数。但它不打印任何东西。错误在哪里? 函数 defsett(i,j) 将两个数字作为所选数字的坐标作为输入,并设置两个整数(w 和 o)作为输入数字的 3x3 部分的起点。 相反,数独函数递归地尝试用数独规则填充矩阵。

# defsett = returns the coordinates of the first cell of the section 3x3 
# where is the cell [i,j]
def defsett(i,j):
    if(0<=i<=2):
        if(0<=j<=2):
            return (0,0)
        elif(3<=j<=5):
            return (0,3)
        elif(6<=j<=8):
            return (0,6)
    elif(3<=i<=5):
        if(0<=j<=2):
            return (3,0)
        elif(3<=j<=5):
            return (3,3)
        elif(6<=j<=8):
            return (3,6)
    else:
        if(0<=j<=2):
            return (6,0)
        elif(3<=j<=5):
            return (6,3)
        elif(6<=j<=8):
            return (6,6)
M = [[5,3,0,0,7,0,0,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]]

# n = matrix side
# i = index of rows
# j = index of cols

def sudoku(n,i,j,M):
        if(i==n):
            print(M)
        elif(j==n):
            j=0
            i=i+1
            sudoku(n,i,j,M)
        else:
            if(M[i][j]==0):
                for number in range(1,n+1):

                    xInRows = False
                    xInCols = False
                    xInSection = False

   # checking if number already present in this row 
                    for k in range(n): 
                        if (number == M[i][k]):
                            xInRows = True

   # checking if number already present in this cols 
                    for k in range(n):
                        if(number == M[k][j]):
                            xInCols = True

                    w,o=defsett(i,j) # first cell of this section

   # checking if number already present in this section 3x3
                    for t in range(w,w+3):
                        for b in range(o,o+3):
                            if(number == M[t][b]):
                                xInSection = True

                    if(not(xInRows) and not(xInCols) and not(xInSection)):
                        M[i][j] = x
                        sudoku(n,i,j+1,M)
            else:
                sudoku(n,i,j+1,M)
sudoku(9,0,0,M)
-----
For this input works:
M = [[5,3,0,0,7,0,0,0,0],
     [6,0,0,1,9,5,0,4,8],
     [1,9,8,3,4,2,5,6,7],
     [8,5,9,7,6,1,4,2,3],
     [4,2,6,8,5,3,7,9,1],
     [7,1,3,9,2,4,8,5,6],
     [9,6,1,5,3,7,2,8,4],
     [2,8,7,4,1,9,6,3,5],
     [3,4,5,0,8,6,1,7,9]]
For this doesn't:
M = [[5,3,0,0,7,0,0,0,0],
     [6,0,0,1,9,5,0,0,8],
     [1,9,8,3,4,2,5,6,7],
     [8,5,9,7,6,1,4,2,3],
     [4,2,6,8,5,3,7,9,1],
     [7,1,3,9,2,4,8,5,6],
     [9,6,1,5,3,7,2,8,4],
     [2,8,7,4,1,9,6,3,5],
     [3,4,5,0,8,6,1,7,9]]

【问题讨论】:

  • 你知道变量可以超过1个字符吗?
  • 解释一下你想说什么……我不明白
  • 你的代码很难理解,因为你的变量名很神秘。
  • 你是对的。对不起。我要编辑它;)

标签: python python-3.x backtracking sudoku recursive-backtracking


【解决方案1】:

什么都不打印的原因很简单:sudoku() 没有取得足够的进展,以至于i == 9 永远产生True。那么让我们来分析一下这是怎么发生的……

  1. i == 9 ⇒ 解决方案退出
  2. i != 9 and j == 9 ⇒ 下一行递归; sudoku(9, i+1, 0, M)
  3. i != 9 and j != 9
    1. M[i][j] != 0 ⇒ 与下一列递归 sudoku(9, i, j+1, M)
    2. M[i][j] == 0 ⇒ 尝试输入一个数字
      1. 找到候选number⇒设置M[i][j] = number不是M[i][j] = x)并进入下一列sudoku(9, i, j+1, M)的递归

这就是整个决策过程。这是M 一旦sudoku() 退出:

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

为了了解出了什么问题,假设您在将 4 放在第八列之后调用 sudoku(9, 0, 8, M)...

1 是唯一还没有放在这一行的数字,但在这里是禁止的,因为它已经出现在第五行 (M[4][8])。 sudoku(9, 0, 8, M) 退出,因为没有其他号码可以尝试。从这里开始,控制在调用堆栈中向上移动,因为没有人能够将 1 放在其他任何地方。为什么 1 如此重要?因为所有其他数字已经放在这一行的某个位置,并且在sudko() 回溯时没有改变!

解决方法很简单:在您填写了数字的递归步骤之后添加一行,以撤消更改以进行正确的回溯。

(...)
                    if(not(xInRows) and not(xInCols) and not(xInSection)):
                        M[i][j] = x
                        sudoku(n,i,j+1,M)
                        M[i][j] = 0
(...)

仅此一项将使sudoku() 完成给定的M。我不保证它会为任何M 找到解决方案。 ;-)

【讨论】: