0

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.

2 Answers2

1

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.

Reti43
  • 9,656
  • 3
  • 28
  • 44
0

Dirty hack: Use the fact that default arguments in Python are shared between function calls:

def Backtrack(board, col, counter = [0]):

  counter[0]+=1
  print('here', counter[0])

Proper solution: Contain your method in a class:

class Board():
    counter = 0

    def Backtrack(self, board, col):

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

later called like so:

Board().Backtrack(board, 0)
matszwecja
  • 6,357
  • 2
  • 10
  • 17
  • Both approaches have the issue that you need to reset `counter` manually if you want to rerun the algorithm. – Reti43 Jan 17 '22 at 12:29
  • @Reti43 I changed the call in proper solution to fix that. – matszwecja Jan 17 '22 at 12:31
  • That addresses the second approach, though it may not be the best way to handle the reset as for every solution you have to instantiate the class. But the bigger issue here is with the dirty hack. [See here](https://stackoverflow.com/a/11416002/2243104). – Reti43 Jan 17 '22 at 12:39