Reputation: 15
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)
I want to keep a track of how many times the recursive backtracking algorithm is called.
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.
Upvotes: 0
Views: 744
Reputation: 7970
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)
Upvotes: 0
Reputation: 9796
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.
Upvotes: 1