0

我正在写一个数独回溯求解器,它卡住了,我不明白为什么。我认为我的递归调用没问题。我错过了什么?

输入从 input.txt 文件中读取,网格初始布局在一行中:

输入.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
4

4 回答 4

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,您将不理会它,也不会尝试该单元格的其他选择。

于 2011-06-21T23:06:31.690 回答
3

您的解决方案被卡住了,因为当您使用递归时(优化代码不是很容易,您可以将其设为尾递归,这样堆栈就不会建立,但您没有。虽然这是一个单独的问题)您实际上并不是检查每个可能的分支。

让我给你一个示例场景,你的代码会卡住:

考虑一下这个数独谜题的左上角:

0 0 3
4 5 6
7 8 9
0 2

您的算法将检查 square [0][0] 并发现 1 是合适的。然后它将继续检查方块 [0][1] 并发现 1 不合适。因此,当检查 2 时,它也会发现该列中已经有一个 2。其余的数字都在盒子里。所以它被卡住了,因为它永远不会回溯到以前的决定。

它应该怎么做?它应该是递归的,它应该能够退后一步并改变导致这种情况的决定。“真”递归(即检查有效递归树的所有可能选项)将能够“撤消”上一个决策并尝试下一个决策,即发现 2 可以在左上角,然后继续上。

于 2011-06-21T22:50:53.090 回答
1

固定的:

我拿走了布尔和返回语句:)

顺便说一句,我原来的测试用例似乎没有解决方案,但是这两个输入确实产生了一个:

输入1.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 ;
       }

    }
于 2011-06-21T23:46:26.667 回答
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)。这可能仍然不能解决所有问题,但肯定会更进一步。

于 2011-06-21T22:38:16.607 回答