数独(Sudoku)是一个 9×9 的矩阵,其中用 1 到 9 的数字进行填写,这样每行、每列和每个子矩阵(3×3)都有一个 1 到 9 的数字。如果我们有一个填了一部分的 9×9 矩阵,并且必须要填上其中剩余的每个单元格,这就是一个数独问题。本文提供了用 C、Java 和 Python 中的回溯求解数独问题的方法。
在本文中,我们将介绍一个使用回溯求解数独的算法。如果你不了解回溯,可以从这篇文章中学习一些基本知识。如下给出的一个数独问题。
解如下:
我们可以看到,每行、每列和每个子矩阵(3×3)都包含一个从 1 到 9 之间不同的数字。因此,我们还可以假设,如果一个数独满足如下任何一个标准,则可以认为它被填错了:
任何一行都多次包含某个相同的数字;
任何一列都多次包含某个相同的数字;
任何一个 3×3 的子矩阵都多次包含某个相同的数字。
在回溯过程中,我们首先从一个子解开始,如果这个子解不能给出准确的最终答案,那么我们就需要返回并改进子解。我们要用同样的方法来求解数独问题。我们将采用以下步骤:
如果没有未分配的单元格,那么数独已被求解,我们将返回 true;
否则,我们将用 1 到 9 之间的数字填写未分配的单元格,以便在任何行、列或 3×3 子矩阵中都没有冲突;
现在,我们将尝试填写下一个未分配的单元格,如果这个操作成功,那么我们将返回 true;
否则,我们会返回并修改用来填写单元格的数字。如果没有满足需求的数字,那么我们将返回 false,因为这个数独没有解;
接下来,让我们开始编码吧。
C 语言
Java 语言
Python
代码说明
最初,我们只是为数独制作一个矩阵,并用 0 填写未分配的单元格。因此,矩阵包含数独问题,值为 0 的单元格为空单元格。
print_sudoku() ——这只是一个打印矩阵的函数。
number_unassigned ——该函数用于查找某个空单元格,并设置变量“行”和“列”的值等于该单元格的索引值。在 C 语言中,我们使用指针来修改传递(通过引用传递)给该函数的变量 (row, col) 的值。在 Java 和 Python 中,我们返回一个包含这些值的数组(或 Python 列表)。所以,这个函数告诉我们是否有未分配的单元格。如果有未分配的单元格,这个函数也会告诉我们该单元格的索引。
is_safe(int n, int r, int c) ——该函数检查是否可以将值“n”放入单元格 (r, c) 中。我们首先检查“r”行中是否有值为“n”的单元格( if(matrix[r][i] == n) )。然后,我们再检查“c”列中是否有值为“n”的单元格( if(matrix[i][c] == n) )。最后,我们检查子矩阵。 (r/3)*3 给出了行 r 的起始索引。例如,如果“r”的值是 2,则它在从 (0,0) 开始的子矩阵中。同样,我们可以得到起始列的值是(c/3)*3 。因此,如果一个单元格是 (2,2) ,那么这个单元格就在一个从 (0,0) 开始的子矩阵中,我们可以通过 (c/3)*3 和 (r/3)*3 得到这个值。在得到起始索引之后,我们可以轻松地遍历子矩阵,以检查是否可以将值“n”放入该子矩阵中。
solve_sudoku() ——实际上,这才是使用回溯求解数独的函数。我们首先使用 number_unassigned 函数检查是否有未赋值的单元格,如果没有未赋值的单元格,那么数独问题已求解。number_unassigned 函数也会给出空单元格的索引。因此,如果有空单元格,我们将尝试用 1 到 9 之间的数值来填写此单元格。我们将使用 is_safe 来检查是否可以在该单元格中填写某个特定的值。在找到某个值之后,我们将尝试使用 solve_sudoku 求解剩余的数独问题。如果这个值不能求解剩余问题,我们将返回并尝试将单元格 matrix[row][col]=0; 赋其他值。循环尝试单元格中的其他值。
原文链接:
https://learnworthy.net/solving-sudoku-with-backtracking
评论 1 条评论