Why is this sudoku backtracking getting stuck?

▼魔方 西西 提交于 2019-12-13 06:35:51

问题


I'm writing a sudoku backtracking solver, it's getting stuck and I don't understand why. I think my recursive calls are alright. What I'm missing?

Input is read from input.txt file with the grid initial layout in a single line:

input.txt:

004020000201950070090004852005490001006000900800051300958100020010072608000080500

Edit: I mean 'stuck' as not finishing its solving of the grid

This is a sample output:

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 

Program:

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

回答1:


A key hint on why backtracking isn't happening is that you've got a boolean function called solve whose return value is not being used at all.

In this loop:

     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;
        }
     }

you find a suitable candidate for the current cell, plug it in and then call next. next then calls solve, but after return, you just assume that was the correct choice and return true; So if solve returns false, you're paying no attention to it and not trying the other choices for that cell.




回答2:


Your solution is getting stuck because while you are using recursion (not very easy to optimise your code, you can make it tail recursive so that stack doesn't build up but you haven't. Although that's a separate issue) you are not actually checking each possible branch.

Let me give you an example scenario where your code will get stuck:

Consider this the top left corner of your sudoku puzzle:

0 0 3
4 5 6
7 8 9
0 2

Your algorithm will check square [0][0] and find that 1 is a suitable fit. It will then move on to checking square [0][1] and find that 1 is unsuitable. So too when checking 2 it will find that there is already a 2 in that column. And the rest of the numbers are in the box. So it's stuck, because it will never retrace its steps back to previous decisions.

What should it do? It should recurse, it should be able to step back and change a decisions that led to this scenario. "True" recursion (i.e. one that checks all possible options of a valid recursion tree) would be able to "undo" the last decision and try the next one, namely, finding that 2 can be in the top left corner, and then continuing on.




回答3:


Fixed:

I took away the boolean and return statements :)

BTW, it appears my original test case doesn't have solution, but these two inputs do yield one:

input1.txt

900576813630090002005000900001004730200000008076900500003000600400050091712869005 

input2.txt (this one is harder)

200609050604083000000700000048000200000010000001000490000001000000970103020408005 

code:

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 ;
       }

    }



回答4:


Not sure exactly what you mean by "stuck", is it the code is stuck or it is just not able to solve the puzzle.

Looking briefly at it, I cannot find anything wrong with the code, but at the same time, the way you use the recursion is not really right, but it should run, even if it does not find a solution...

But there are other techniques to solve sudoku puzzles that you are not using, X-Wing may be one of the most sophisticated...

Even following what you are doing now, you'll probably need to run through those tests several times...

Edit:

I run your code, ran just fine, at the last stage, it printed :

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 

and then it was done... it just won't solve anything anymore.

I think instead of using

row>=grid.length

as a termination condition, you should go through the grid to find if there are any '0' left. And in the case row>=grid.length, you should start again : solve(0,0,grid). That may still not solve everything, but will definitely go a bit further.



来源:https://stackoverflow.com/questions/6432794/why-is-this-sudoku-backtracking-getting-stuck

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!