pythonnumpyrecursionbacktrackingn-queens

Keep track of how many times a recursive function has been called in Python


I have a piece of Python code that implements a recursive backtracking algorithm for solving the famous N-Queens problem in chess.

def Backtrack(board, col):

  if col >= N:
    return True

  for i in range(N):
      if (ld[i - col + N - 1] != 1 and rd[i + col] != 1) and cl[i] != 1:
          board[i][col] = 1
          ld[i - col + N - 1] = rd[i + col] = cl[i] = 1
          if Backtrack(board, col + 1):
              return True
          board[i][col] = 0 # Backtrack 
          ld[i - col + N - 1] = rd[i + col] = cl[i] = 0

  return False

where

ld = np.zeros(2*N - 1, dtype=int)
rd = np.zeros(2*N - 1, dtype=int)
cl = np.zeros(N, dtype=int)
board = np.zeros((N, N), dtype=int)

The Problem:

I want to keep a track of how many times the recursive backtracking algorithm is called.

My Attempt:

I added a counter variable to the code such that

def Backtrack(board, col, counter):

  counter += 1
  print('here', counter)

  if col >= N:
    return True

  for i in range(N):
      if (ld[i - col + N - 1] != 1 and rd[i + col] != 1) and cl[i] != 1:
          board[i][col] = 1
          ld[i - col + N - 1] = rd[i + col] = cl[i] = 1
          if Backtrack(board, col + 1, counter):
              return True
          board[i][col] = 0 # Backtrack 
          ld[i - col + N - 1] = rd[i + col] = cl[i] = 0

  return False

however for N = 4 the output is

here 1
here 2
here 3
here 3
here 4
here 2
here 3
here 4
here 5

which shows that my attempt is incorrect. The function is called 9 times but at the end the counter variable is 5.


Solution

  • Integers are immutable, so when you do counter += 1, you create a new number, say from 1 to 2, and 2 is now bound to the name counter. So when you're at depth 2 and call the function twice, both calls will increment 2 to their own 3. In python you don't pass by variable, but by name, so that counter isn't referring to the same thing throughout all the calls.

    What you want is either a mutable or global variable. For example

    # or a class implementation equivalent
    counter = 0
    def backtrack(board, col):
        global counter
        counter += 1
        # the rest
    

    But that way you'd need to reset counter to 0 every time you want to restart the algorithm. Therefore, I think it's better to do

    def backtrack(board, col, counter=None):
        if not counter:
            counter = [0]
        counter[0] += 1
        # the rest of your function
    

    Be very careful of doing backtrack(board, col, counter=[0]), because counter will be initialised to the list only once. After you've solved, for say N=4, it will have the value 9 and if you call the function again, it will keep incrementing from there.