繁体   English   中英

Java Sudoku求解器回溯

[英]Java Sudoku solver backtracking

我正在尝试构建数独求解器。 我知道我的代码很乱,可能会有更简单的方法来做,但是我想以开始的方式来完成算法。

该算法开始执行我想要的操作(用适合的第一个数字填充空白),但是当到达没有选项的点时,我不知道如何返回并擦除我尝试插入的最后一个数字另一种组合。 但是我不能只删除矩阵中的最后一个数字,因为它可能是算法未放置的数字。

如果有人可以提供帮助,我将非常感激。

public class Backtracking{

public static void Sudoku(int[][] sudokuTable){
    if (isAnswer(sudokuTable)){
        printSudoku(sudokuTable);
    }else{
        for (int j = 1; j <=9; j++){
            if (canfit(sudokuTable, j)){
                addToSudoku(sudokuTable, j);
                printSudoku(sudokuTable);
                Sudoku(sudokuTable);
            }
        }
    }
}

public static void addToSudoku(int[][] sudokuTable, int n){
    int i = 0;
    int j = 0;
    boolean done = false;
    while (i < 9 && !done){
        while (j < 9 && !done){
            if (sudokuTable[i][j] == 0){
                sudokuTable[i][j] = n;
                done = true;
            }
            j++;
        }
        i++;
    }
}

public static void printSudoku(int[][] sudokuTable){
    for (int i = 0; i < 9; i++){
        for (int j = 0; j < 9; j++){
            System.out.print(sudokuTable[i][j] + " ");
        }
        System.out.println();
    }
    System.out.println();
}

public static boolean isAnswer(int[][] sudokuTable){
    int sum = 0;
    for (int i = 0; i < 9; i++){
        for (int j = 0 ; j < 9; j++){
            if (sudokuTable[i][j] > 9 || sudokuTable[i][j] < 1)
                return false;
            else
                sum++;
        }
    }
    if (sum != 405)
        return false;
    return true;
}

public static boolean canfit(int[][] sudokuTable, int n){
    int i = 0;
    int j = 0;
    boolean pos = false;
    boolean fit = true;
    while (i < 9 && !pos){
        while (j < 9 && !pos){
            if (sudokuTable[i][j] == 0)
                pos = true;
            else
                j++;
        }
        if (!pos)
            i++;
    }
    for (int k = 0; k < 9; k++){
        if (sudokuTable[i][k] == n && k != j)
            fit = false;
    }
    if (fit){
        for (int l = 0; l < 9; l++){
            if(sudokuTable[l][j] == n && l != i)
                fit = false;
        }
    }
    if (fit){
        if (i >= 0 && i < 3)
            i = 0;
        else if (i >=3 && i < 6)
            i = 3;
        else if (i >=6 && i < 9)
            i = 6;
        if (j >= 0 && j < 3)
            j = 0;
        else if (j >=3 && j < 6)
            j = 3;
        else if (j >=6 && j < 9)
            j = 6;
        for (int m = i; m < i+3; m++){
            for (int o = j; o < j+3; o++){
                if (sudokuTable[m][o] == n)
                    fit = false;
            }
        }
    }
    return fit;
}

尝试从Sudoko方法返回true或false。

isAnswer()方法返回true ,将打印表。 然后从Sudoko()方法返回true

现在在for循环中,您递归地调用Sudoko()方法,检查它是否返回truefalse 如果返回true ,则表明您的选择是正确的,并且可以找到解决方案,您无需执行其他任何操作。 如果返回false ,则删除使用addToSudoko()方法设置的数字。 在调用addToSudoko()方法并继续进行迭代之前,使表addToSudoko()

如果您的for循环,循环9次,但没有一个数字有合适的位置,这意味着如果循环结束,则返回false。

希望这可以帮助

实际上,您可以使用数组来回溯移动,每次移动错误时,您都只是开始删除一些移动并尝试其他移动,但是这有一个问题:

  • 尝试所有可能的移动的复杂性非常大(尝试用81位数字尝试所有数字要花费多长时间?即使您在这里和那里削减了计算时间,也将需要整个宇宙的时间)
  • 数独的主要问题是您不知道如果随机移动,这是错误的举动。

假设以下情况是2x2的单元格sudoky:

+----+----++-----+-----+
| 1  |  2 ||  3  |  4  |
+----+----++-----+-----+
| 4  |  3 ||  1  |  2  |
+====+====++=====+=====+
| 2  |  1 ||  4  |  3  |
+----+----++-----+-----+
| 3  |  4 ||  2  |  1  |
+----+----++-----+-----+

如果在以下(未解决的)情况下使用算法:

+----+----++-----+-----+
| 1  |    ||     |  4  |
+----+----++-----+-----+
|    |    ||     |     |
+====+====++=====+=====+
| 2  |    ||     |     |
+----+----++-----+-----+
|    |  4 ||     |  1  |
+----+----++-----+-----+

他可能会遇到以下顺序

+----+----++-----+-----+
| 1  |    ||  2  |  4  |
+----+----++-----+-----+
|    |    ||  2  |     |
+====+====++=====+=====+
| 2  |    ||     |     |
+----+----++-----+-----+
|    |  4 ||     |  1  |
+----+----++-----+-----+

实际上添加的“ twos”都是错误的,但是当您的算法发现错误的原因是因为在同一列中有2个“ twos”时, 您不知道哪个是错误的 (第一个添加了,第二个添加了) , 或两者?)

正确的回溯算法将像这样工作:

  • 您从一个81个单元格的数组开始,然后开始按顺序放置数字。

 for(int i=0; i<81; i++)
    array[i] = TryNumber();

  • 然后您检查每个添加的数字是否正确。

if(SomeNumberNotCorrect())
    break;  // you know you can stop "tryingnumbers" so you can save on loop executions

  • 但是你不知道哪个是错的

现在写一个正确的算法来解决数独问题,并且不能在数百万年内运行,这已经很长了,我不能在这里写出来,但是我不认为您选择的方式是最好的。 最好的方法是应用玩家使用的相同策略。 请记住,如果您想让算法以与计算机下棋方式类似的方式来解决数独问题,则算法将比棋类游戏花费更多的时间(实际上,计算机不能分析棋盘动作超过5-6圈) 。 但是实际上数独可以用更快的算法解决。

相反,我过去已经做过一个简单的求解器,您实际上可以尝试应用玩家用来解决它的相同策略:

例如:

  • 对于每个单元格,检查当前Sector,Column和Line是否已经具有除1以外的所有数字,然后可以添加该数字。 并检查每个单元格只需要81 * 81 * 81个移动。

当您的求解器不再在Sudoku上找到解决方案时,这仅仅是因为您必须作为玩家开发一种新策略,然后将其应用到您的程序中,总之,您将拥有一个能够解决所有问题的程序。数独(不是很困难,实际上这是有很多免费的数独求解器的原因)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM