Sudoku solver in Java, using backtracking and recursion

后端 未结 6 483
隐瞒了意图╮
隐瞒了意图╮ 2020-12-09 16:01

I am programming a Sudoku solver in Java for a 9x9 grid.

I have methods for:

  • printing the grid

  • initializing the board with given val

6条回答
  •  醉梦人生
    2020-12-09 16:37

    The other answers on this page have covered Backtracking Algorithm. Interestingly, with just a bit optimization, you can improve this backtracking algorithm significantly. The idea is to use Greedy Best-First Search: Instead of picking the 'next' cell top to bottom, left to right, you pick the next cell as the cell with the least number of possibilities.

    For example, if the row containing that cell has already had number 1 2 3, the column's had 4 5 6, and the 3x3 block's had 7, then there are only 2 possibilities left: 8 and 9. This looks like a pretty good cell to pick.

    This improvement speeds up the program quite a bit and makes the program run fast enough for my Real-time Sudoku Solver

    You can check out the animation of this algorithm here.

    Link to Visualizer Code and Real-time Solver Code

    The code for Greedy Best First Search is below:

    # Keep data about the "Best" cell
    class EntryData:
        def __init__(self, r, c, n):
            self.row = r
            self.col = c
            self.choices = n
    
        def set_data(self, r, c, n):
            self.row = r
            self.col = c
            self.choices = n
    
    # Solve Sudoku using Best-first search
    def solve_sudoku(matrix):
        cont = [True]
        # See if it is even possible to have a solution
        for i in range(9):
            for j in range(9):
                if not can_be_correct(matrix, i, j): # If it is not possible, stop
                    return
        sudoku_helper(matrix, cont) # Otherwise try to solve the Sudoku puzzle
    
    # Helper function - The heart of Best First Search
    def sudoku_helper(matrix, cont):
        if not cont[0]: # Stopping point 1
            return
    
        # Find the best entry (The one with the least possibilities)
        best_candidate = EntryData(-1, -1, 100)
        for i in range(9):
            for j in range(9):
                if matrix[i][j] == 0: # If it is unfilled
                    num_choices = count_choices(matrix, i, j)
                    if best_candidate.choices > num_choices:
                        best_candidate.set_data(i, j, num_choices)
    
        # If didn't find any choices, it means...
        if best_candidate.choices == 100: # Has filled all board, Best-First Search done! Note, whether we have a solution or not depends on whether all Board is non-zero
            cont[0] = False # Set the flag so that the rest of the recursive calls can stop at "stopping points"
            return
    
        row = best_candidate.row
        col = best_candidate.col
    
        # If found the best candidate, try to fill 1-9
        for j in range(1, 10):
            if not cont[0]: # Stopping point 2
                return
    
            matrix[row][col] = j
    
            if can_be_correct(matrix, row, col):
                sudoku_helper(matrix, cont)
    
        if not cont[0]: # Stopping point 3
            return
        matrix[row][col] = 0 # Backtrack, mark the current cell empty again
                
    
    # Count the number of choices haven't been used
    def count_choices(matrix, i, j):
        can_pick = [True,True,True,True,True,True,True,True,True,True]; # From 0 to 9 - drop 0
        
        # Check row
        for k in range(9):
            can_pick[matrix[i][k]] = False
    
        # Check col
        for k in range(9):
            can_pick[matrix[k][j]] = False;
    
        # Check 3x3 square
        r = i // 3
        c = j // 3
        for row in range(r*3, r*3+3):
            for col in range(c*3, c*3+3):
                can_pick[matrix[row][col]] = False
    
        # Count
        count = 0
        for k in range(1, 10):  # 1 to 9
            if can_pick[k]:
                count += 1
    
        return count
    
    # Return true if the current cell doesn't create any violation
    def can_be_correct(matrix, row, col):
        
        # Check row
        for c in range(9):
            if matrix[row][col] != 0 and col != c and matrix[row][col] == matrix[row][c]:
                return False
    
        # Check column
        for r in range(9):
            if matrix[row][col] != 0 and row != r and matrix[row][col] == matrix[r][col]:
                return False
    
        # Check 3x3 square
        r = row // 3
        c = col // 3
        for i in range(r*3, r*3+3):
            for j in range(c*3, c*3+3):
                if row != i and col != j and matrix[i][j] != 0 and matrix[i][j] == matrix[row][col]:
                    return False
        
        return True
    
    # Return true if the whole board has been occupied by some non-zero number
    # If this happens, the current board is the solution to the original Sudoku
    def all_board_non_zero(matrix):
        for i in range(9):
            for j in range(9):
                if matrix[i][j] == 0:
                    return False
        return True
    

提交回复
热议问题