我正在写一个数独的回溯解算器,它已经卡住了,我不明白为什么。我认为我的递归调用没问题。我错过了什么?
输入从input.txt文件中读取,网格初始布局在一行中:
input.txt中:
004020000201950070090004852005490001006000900800051300958100020010072608000080500
编辑:我的意思是“卡住”因为没有完成对网格的解决
这是一个示例输出:
current move count is 6
3 6 4 7 2 8 1 9 0
2 0 1 9 5 0 0 7 0
0 9 0 0 0 4 8 5 2
0 0 5 4 9 0 0 0 1
0 0 6 0 0 0 9 0 0
8 0 0 0 5 1 3 0 0
9 5 8 1 0 0 0 2 0
0 1 0 0 7 2 6 0 8
0 0 0 0 8 0 5 0 0
程序:
package sudoku;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class Main {
static boolean checkRow( int row, int num, int grid[][])
{
for( int col = 0; col < 9; col++ )
if( grid[row][col] == num )
return false ;
return true ;
}
static boolean checkCol( int col, int num, int grid[][] )
{
for( int row = 0; row < 9; row++ )
if( grid [row][col] == num )
return false ;
return true ;
}
static boolean checkBox( int row, int col, int num, int grid[][] )
{
row = (row / 3) * 3 ;
col = (col / 3) * 3 ;
for( int r = 0; r < 3; r++ ){
for( int c = 0; c < 3; c++ ){
if( grid[row+r][col+c] == num )
return false ;
}
}
return true ;
}
static void printSolvedGrid(int grid[][]){
for (int i=0; i<grid.length; i++){
for (int j=0; j<grid.length;j++){
System.out.print(grid[i][j]+" ");
} System.out.println();
}
}
static int moveCounter=0;
static boolean solve(int row, int col, int [][]grid){
if (row>=grid.length){
System.out.println("solution found");
printSolvedGrid(grid);
}
if( grid[row][col] != 0 ){
next( row, col, grid ) ;
}
else {
// Find a valid number for the empty cell
for( int num = 1; num < 10; num++ )
{
if( checkRow(row,num,grid) && checkCol(col,num,grid) && checkBox(row,col,num,grid) )
{
grid[row][col] = num ;
moveCounter++;
System.out.println("current move count is " + moveCounter);
printSolvedGrid(grid);
next( row, col, grid );
return true;
}
}
}
return false;
}
public static void next( int row, int col, int [][] grid )
{
if( col < 8 ) //pass to next col
solve( row, col + 1, grid ) ;
else //pass to next row
solve( row + 1, 0, grid ) ;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(new File("input.txt")));
char gridChar[] = br.readLine().toCharArray();
int [][] grid = new int [9][9];
int gridCharIndex=0;
for (int i=0; i<grid.length; i++){
for (int j=0; j<grid.length;j++){
grid[i][j]= Integer.parseInt(gridChar[gridCharIndex++]+"");
System.out.print(grid[i][j]+" ");
} System.out.println();
}
solve(0,0, grid);
}//end method main
}//end class Main
答案 0 :(得分:5)
关于为什么不进行回溯的关键提示是你有一个名为solve
的布尔函数,它的返回值根本就没用过。
在这个循环中:
for( int num = 1; num < 10; num++ )
{
if( checkRow(row,num,grid) && checkCol(col,num,grid) && checkBox(row,col,num,grid) )
{
grid[row][col] = num ;
moveCounter++;
System.out.println("current move count is " + moveCounter);
printSolvedGrid(grid);
next( row, col, grid );
return true;
}
}
您找到当前单元格的合适候选者,将其插入然后调用next
。 next
然后调用solve
,但在返回后,您只是假设这是正确的选择而return true;
因此,如果solve
返回false,则您不会注意它,没有尝试那个单元格的其他选择。
答案 1 :(得分:3)
你的解决方案被卡住了,因为当你使用递归时(不是很容易优化你的代码,你可以使它递归尾部,这样堆栈就不会积累,但你没有。虽然这是一个单独的问题)你实际上没有检查每个可能的分支。
让我举个例子,你的代码会被卡住:
请考虑这是数独谜题的左上角:
0 0 3
4 5 6
7 8 9
0 2
您的算法将检查square [0] [0]并找到1是合适的。然后它将继续检查square [0] [1]并发现1不合适。检查2时也会发现该列中已有2个。其余的数字都在盒子里。所以它被卡住了,因为它永远不会回溯到以前的决定。
该怎么办?它应该 recurse ,它应该能够退后一步并改变导致这种情况的决策。 “真正的”递归(即检查有效递归树的所有可能选项的递归)将能够“撤消”最后的决定并尝试下一个决策,即发现2可以在左上角,然后继续上。
答案 2 :(得分:1)
修正:
我拿走了boolean和return语句:)
顺便说一句,看来我的原始测试用例没有解决方案,但这两个输入确实产生一个:input1.txt
900576813630090002005000900001004730200000008076900500003000600400050091712869005
input2.txt(这个更难)
200609050604083000000700000048000200000010000001000490000001000000970103020408005
代码:
static void solve(int row, int col, int [][]grid){
if (row>=grid.length){
System.out.println("solution found");
printSolvedGrid(grid);
System.exit(0);
}
if( grid[row][col] != 0 ){
next( row, col, grid ) ;
}
else {
// Find a valid number for the empty cell
for( int num = 1; num < 10; num++ )
{
if( checkRow(row,num,grid) && checkCol(col,num,grid) && checkBox(row,col,num,grid) )
{
grid[row][col] = num ;
moveCounter++;
System.out.println("current move count is " + moveCounter);
printSolvedGrid(grid);
next( row, col, grid );
}
}
grid[row][col] = 0 ;
}
}
答案 3 :(得分:0)
不确定“卡住”究竟是什么意思,是代码卡住还是无法解决这个难题。
简单地看一下,我发现代码没有任何问题,但与此同时,你使用递归的方式并不正确,但它应该运行,即使它没有找到解决方案......
但是还有其他技巧来解决你没有使用的数独谜题,X-Wing可能是最复杂的......
即使你现在正在做什么,你可能需要多次完成这些测试......
修改强>:
我运行你的代码,运行得很好,在最后一个阶段,打印出来:
current move count is 6
3 6 4 7 2 8 1 9 0
2 0 1 9 5 0 0 7 0
0 9 0 0 0 4 8 5 2
0 0 5 4 9 0 0 0 1
0 0 6 0 0 0 9 0 0
8 0 0 0 5 1 3 0 0
9 5 8 1 0 0 0 2 0
0 1 0 0 7 2 6 0 8
0 0 0 0 8 0 5 0 0
然后就完成了......它再也无法解决任何问题了。
我认为不是使用
row>=grid.length
作为终止条件,您应该通过网格查找是否还有“0”。在row>=grid.length
的情况下,您应该重新开始:solve(0,0,grid)
。
这可能仍然无法解决所有问题,但肯定会更进一步。