1
def answer_solve_sudoku(__grid):

    res = check_sudoku(__grid)
    if res is None or res is False:
        return res

    grid = copy.deepcopy(__grid)

    # find the first 0 element and change it to each of 1..9,
    # recursively calling this function on the result
    for row in xrange(9):
        for col in xrange(9):
            if grid[row][col] == 0:
                for n in xrange(1, 10):
                    grid[row][col] = n
                    new = answer_solve_sudoku(grid)
                    if new is not False:
                        return new
                # backtrack
                return False

    # if we get here, we found no zeros and so we're finished
    return grid

Here is the code, and check_sudoku(grid) can return if a grid is a valid sudoku or not.

I just can't understand the recursion part, I tried to write down the process on the paper, but it failed everytime, how is backtraking working? and what is new? if the answer_solve_sudoku(grid) is valid?

I know it sets every 0 to 1..9, and checks if it's a valid grid or not, but I just can't draw the whole process on the paper. And can't really understand how the backtrack is working.

btw, is there any advice of understanding recursion code?

Best Regards,

Sheng Yun

EDIT

I read the code again and again, and now I have some understanding, but I'm just not that sure about this, it will be kind if anyone gives me some comments.

1, return new will only be called when the solver found the solution, and this will be called right after return grid

2, when will

# backtrack
return False

be called? if the next solution isn't right, check_sudoku(__grid) will return False, and if the next solution is right, it will call another answer_solve_sudoku(grid) till it gets the right solution, and when it gets the right solution, it will return grid and then return new. So when is:

# backtrack
return False

called?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
shengy
  • 9,461
  • 4
  • 37
  • 61
  • its easier to see how backtracking works with a smaller problem .. like 4 queens ... check out http://www.academic.marist.edu/~jzbv/algorithms/Backtracking.htm – Joran Beasley Jul 17 '12 at 05:10
  • Strange coincidence... Just answered on this one 2 days ago: http://stackoverflow.com/q/11486358/496445 , same topic, same function names. – jdi Jul 17 '12 at 05:11
  • @jdi Maybe it's because of: http://www.udacity.com/view#Course/cs258/CourseRev/1/Unit/139002/Nugget/273001 – shengy Jul 17 '12 at 05:11
  • Ah. Yes maybe you are both in the same class :-) – jdi Jul 17 '12 at 05:12
  • You may already be doing this, but when writing something out on paper, it's helpful to run through the program line by line to make sure you are writing what the program is doing and not what you think it's doing. I like to do this on a whiteboard so I can write out a list of variables and update their values like the program would. – Matthew Adams Jul 17 '12 at 05:26
  • @jdi could you help me do some comments to my new understanding? I'm just not that sure. – shengy Jul 17 '12 at 06:24
  • I found a solution at **activestate python**, https://code.activestate.com/recipes/576725-brute-force-sudoku-solver/ Just ignore it if no help. – staticor May 27 '15 at 05:31

2 Answers2

1

Rather than writing this out on paper, I have a better recommendation. Format the code to show you a visual representation of what the logic is doing. Here is a way to do that:

def print_counter(val, msg):
    print "%s[%d] %s" % (" "*val, val, msg)

def answer_solve_sudoku(__grid, counter=0):

    res = check_sudoku(__grid)
    if res is None or res is False:
        return res

    grid = copy.deepcopy(__grid)

    for row in xrange(9):
        for col in xrange(9):
            if grid[row][col] == 0:
                for n in xrange(1, 10):
                    grid[row][col] = n
                    print_counter(counter,"test: (row %d, col %d) = %d" % (row,col,n))
                    new = answer_solve_sudoku(grid, counter+1)
                    if new is not False:
                        print_counter(counter, "answer_solve_sudoku() solved: returning")
                        return new
                # backtrack
                print_counter(counter, "backtrack")
                return False

    print_counter(counter, "**SOLVED! Returning back up to top**")
    return grid

from pprint import pprint 
solution = answer_solve_sudoku(easy_grid)
pprint(solution)

What I did was create a little printer function that will print a number and indent the message by that many spaces. Then in your answer_solve_sudoku, I gave it a default counter value of 0, and always pass in counter+1 to each recursive call. That way as the depth grows, so will the number. And I put printer functions along to way to visually illustrate what is happening.

What you will see is something like this:

[0] test: (row 0, col 2) = 1
[0] test: (row 0, col 2) = 2
[0] test: (row 0, col 2) = 3
[0] test: (row 0, col 2) = 4
 [1] test: (row 0, col 3) = 1
  [2] test: (row 0, col 4) = 1
  [2] test: (row 0, col 4) = 2
  [2] test: (row 0, col 4) = 3
    ... 
         [45] test: (row 7, col 7) = 8
         [45] test: (row 7, col 7) = 9
         [45] backtrack
        [44] test: (row 7, col 5) = 6
        [44] test: (row 7, col 5) = 7
    ... 
               [51] test: (row 8, col 6) = 6
               [51] test: (row 8, col 6) = 7
                [52] **SOLVED! Returning back up to top**
               [51] answer_solve_sudoku() solved: returning
              [50] answer_solve_sudoku() solved: returning
             [49] answer_solve_sudoku() solved: returning
    ... 
  [2] answer_solve_sudoku() solved: returning
 [1] answer_solve_sudoku() solved: returning
[0] answer_solve_sudoku() solved: returning

return new will only be called when the solver found the solution, and this will be called right after return grid

Yes, when a call to answer_solve_sudoku makes its way through that whole loop without failing and reaches the bottom, it has succeeded and returns the grid. The caller will then get that grid as a result to new = answer_solve_sudoku(grid) and return that. The grid will make its way back up each returning call on the stack.

when will backtrack occur?

Because you are creating a copy of the grid in each recursion, unless that step finds the solution, the changes it made to that grid will be discarded, since once we return back up one step we will be back to our previous grid state. It tries to go as far as it can with that solution until it runs past the value of 9.

jdi
  • 90,542
  • 19
  • 167
  • 203
1

You should check out this answer. It has pseudocode and actual code for recursive backtracking. The code is in Java, but it is the same thought process for Python.

Community
  • 1
  • 1
theDazzler
  • 1,039
  • 2
  • 11
  • 27