Sudoku backtracking algorithm (Java)

2019-08-02 07:07发布

问题:

I've created a Sudoku solver that will solve a Sudoku as a human might- by checking possibilities + definite values in squares corresponding to the square being checked.

(Source: http://pastebin.com/KVrXUDBF)

However, I would like to create a random Sudoku generator (from a blank grid), and so have decided to use a backtracking algorithm. I understand the concept of backtracking, but am confused about one thing:

How do I know which previous node to return to (and change) once I know a certain solution is not allowed? Should I simply return to the previous node and cycle through all possibilities? (And then if this yields no correct answers, return to the value before, etc.). This seems like a viable method, but also quite inefficient. Is this the correct way of implementing a backtracking method or is there a better way to go about it?

Thanks in advance.

More can be found about backtracking here: http://en.wikipedia.org/wiki/Backtracking

回答1:

Sudoku Puzzle can be reduced to graph coloring problem which can be solved using simple backtracking like assigning colors to node (1-9) till the there is no violation that all directly connected nodes have no same color.

Constructing Graph from Sudoku : -

There is an direct edge between two grid points if they are in same row or column or square.

Backtracking :-

  1. Assign one color (1-9) to node
  2. Check if there is no other directly connected node with same color
  3. If valid color move to next node.
  4. else change the color and recheck.
  5. If all color exhausted backtrack to previous node.
  6. Do recursion till all nodes are color.

Once You are done with it you can start removing numbers from the grid at random till you think the problem is unsolvable if any more numbers are removed.



回答2:

A simple way to generate random Sudoku is that
1) generate a random completing Sudoku, that is, generate random Sudoku no square is blank.
2) Remove numbers from squares of 1).
3) Solve Sudoku of 2). If there are many solutions, then add a number removed at 2).
If there are still many solutions, then repeat 3).

1) sample source code:

public int[][] generateRandomCompleteSudoku() {
  int[][] sudoku = new int[10];
  for(int i = 1; i <= 9; i++) {
    sudoku[i] = new int[10];
    Arrays.fill(sudoku[i], 0);
  }
  generateRandomCompleteSudoku(sudoku, 1, 1);
  return sudoku;
}
private boolean generateRandomCompleteSudoku(int[][] sudoku, int x, int y) {
  if(x > 9) {
    x = 1;
    y++;
  }
  //sudoku of the argument is completing sudoku.
  //so return true
  if(y > 9) {
    return true;
  }
  // enumerate the possible numbers of the pos(x,y). 
  List<Integer> possibleNumbers = new ArrayList<Integer>();
  for(int i = 1; i <= 9; i++) {
    boolean possible = true;
    //check i is a possible number.
    //check there isn't i in the raw of y .
    for(int j = 1; j <= x - 1; j++) {
      if(sudoku[j][y] == i) {
        possible = false;
        break;
      }
    }
    //check there isn't i in the column of x(omitted).
    //check there isn't i in the group of x,y(omitted).
    if(possible) {
      possibleNumbers.add(i);
    }
  }
  //sudoku is wrong so return false.(There is no solution of sudoku)
  if(possibleNumbers.size() <= 0) {
    return false;
  }
  Collections.shuffle(possibleNumbers);// This gives sudoku randomness.
  for(Integer possibleNumber : possibleNumbers) {
    sudoku[x][y] = possibleNumber;
    // a sudoku is generated, so return true
    if(generateRandomCompleteSudoku(sudoku, x + 1, y)) {
       return true;
    }
  }
  // No sudoku is generated, so return false
  return false;
}


回答3:

For a backtracking solution, the first step is to define the state. So for this problem, I think the most straightforward way is (x,y, blank , num) with x , y is the position of the current state, blank is the number of blank position left, and num is the value you want to fill in that position (from 0 to 9 and 0 means blank).

And the return type should be boolean, which determine whether the move is valid or not (which means is there any valid solution for this move).

So, the state transition is column by column, row by row: x, y to x , (y + 1) or x , y to (x + 1), 0. Similarly, the blank will be from a -> a - 1-> ... 0. We have a draft solution here:

public boolean move(int x, int y, int blank, int num, int[][]sudoku){
    sudoku[x][y] = num;
    //checking condition and return if x,y is the last position, code omitted
    if(y == sudoku[x].length){
            x++;
            y = 0;
    }else{
            y++;
    }
    for(int i = 1; i < 10; i++){
            if(move(x,y,blank,i,sudoku){//Backtrack here
                    return true;
            }
    }
    if(blank > 0){
            if(move(x,y,blank - 1, 0, sudoku){//Backtrack here
                    return true;
            }
    }
    return false;


}

So when ever there is a false return from the current state, it will backtrack to the last state , and the last state will continue to check for the next num until it find a correct solution (or return false).