数独回溯与解决方案计数器

时间:2020-05-28 11:14:24

标签: java backtracking sudoku

背景

我已经实现了一个数独求解器算法(回溯),如下所示:

//Backtracking-Algorithm
public static boolean solver(int[][] board) {
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (board[i][j] == 0) {
                for (int n = 1; n < 10; n++) {
                    if (checkRow(board, i, n) && checkColumn(board, j, n) && checkBox(board, i, j, n)) {
                        board[i][j] = n;
                        if (!solver(board)) {
                            board[i][j] = 0;
                        } else {
                            return true;
                        }
                    }
                }
                return false;
            }
        }
    }
    return true;
}

此解决方案运行良好(可以解决数独问题)。

我要实现的目标

我现在想要实现该算法告诉我,是只有一个解决方案还是多个解决方案。

我尝试过的事情

我试图通过将返回类型更改为int并计算可能的解决方案来实现我的目标(停在2,因为如果有两个解决方案,我可以说有“多个”解决方案)。因此,基本上,我只想知道是否有一个,一个或多个解决方案:

// Backtracking-Algorithm
public int solver(int[][] board, int count) { //Starts with count = 0
  if (count < 2) {
    for (int i = 0; i < GRID_SIZE; i++) {
      for (int j = 0; j < GRID_SIZE; j++) {
        /*
         * Only empty fields will be changed
         */
        if (board[i][j] == EMPTY) {
          /*
           * Try all numbers between 1 and 9
           */
          for (int n = 1; n <= GRID_SIZE; n++) {
            /*
             * Is number n safe?
             */
            if (checkRow(board, i, n) && checkColumn(board, j, n) && checkBox(board, i, j, n)) {
              board[i][j] = n;
              if (solver(board, count) > count) {
                count++;
              } else {
                board[i][j] = 0;
              }
            }
          }
          return count;
        }
      }
    }
    return count + 1;
  }
  return count;
}

问题是count始终为“ 1”,然后算法停止。

问题

要使其正常运行,必须对代码进行哪些更改?

2 个答案:

答案 0 :(得分:3)

您的代码存在的问题是它在找到第一个解决方案后便停止了-更具体地说,除非您错了,否则您的代码将永远不会更改为单元格分配的值。这是您已实现的标准回溯。您需要做的是,一旦找到一个解决方案,就需要强制您的代码使用其他值,并查看它是否还返回有效的解决方案。

比方说,这是数独的最后一行(您遗漏了最后一个值),当前计数为0(即到目前为止没有解决方案):

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

您的代码将为最后一个单元格尝试1-9中的所有值,一旦发现9是正确的值,它将填充该值并进行递归调用。

在递归调用中,您的代码将找不到任何空值,因此它将使count递增1(因此count现在为1)并返回,特别是以下行:return count + 1;因为您没有做任何进一步的操作此时进行递归调用,将增加的计数沿递归堆栈传递,最终结果为1。

您需要做的是,找到一种解决方案后,您需要再次回溯并强制增加其中一个值。您在解决方案中找到的最后一行如下:

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

您不能增加最后一个单元格,因为它已经是9,因此将其设置为0 / EMPTY并转到上一个值。先前的值是8,可以增加到9,因此您可以执行以下操作,然后求解该板:

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

也许这不会返回解决方案,所以您再返回一个(将倒数第二个单元格设置为0并增加前一个单元格:

| 1 | 2 | 3 | 4 | 5 | 6 | 8 | 0 | 0 |

现在看看是否能为您提供解决方案。等等...

TLDR:找到解决方案后,您需要将其反馈给具有更严格约束的代码(即,强制增加有效值之一,并查看它是否仍为您提供另一种解决方案)。

答案 1 :(得分:0)

多亏了阿齐兹·索纳瓦拉(Aziz Sonawalla)的this回答,我想我已经知道了。

以下实现能够解决给定here的唯一可解决的数独问题。而且,该算法现在可以用多个解决方案(example)来解决数独问题,并且可以识别出不止一种解决方案。如果是这种情况,该程序将仅给出可能的解决方案之一。

代码如下:

// Backtracking-Algorithm
public int[][] board2 = new int[GRID_SIZE][GRID_SIZE];

public int solver(int[][] board, int count) { // Starts with count = 0

    for (int i = 0; i < GRID_SIZE; i++) { //GRID_SIZE = 9

      for (int j = 0; j < GRID_SIZE; j++) {

        /*
         * Only empty fields will be changed
         */

        if (board[i][j] == EMPTY) { //EMPTY = 0

          /*
           * Try all numbers between 1 and 9
           */

          for (int n = 1; n <= GRID_SIZE && count < 2; n++) {

            /*
             * Is number n safe?
             */
            if (checkRow(board, i, n) && checkColumn(board, j, n) && checkBox(board, i, j, n)) {

              board[i][j] = n;
              int cache = solver(board, count);
              if (cache > count) {
                count = cache;
                for (int k = 0; k < board.length; k++) {
                  for (int l = 0; l < board.length; l++) {
                    if (board[k][l] != EMPTY) {
                      board2[k][l] = board[k][l];
                    }

                  }
                }

                board[i][j] = EMPTY;

              } else {
                board[i][j] = EMPTY;
              }

            }
          }
          return count;
        }
      }
    }
    return count + 1;
}

解决方案现在保存在数组board2中。

据我所知,此实现按预期工作。如果发现任何错误,请发表评论。