Backtracking engine:
def solve(values, safe_up_to, size):
"""Finds a solution to a backtracking problem.
values -- a sequence of values to try, in order. For a map coloring
problem, this may be a list of colors, such as ['red',
'green', 'yellow', 'purple']
safe_up_to -- a function with two arguments, solution and position, that
returns whether the values assigned to slots 0..pos in
the solution list, satisfy the problem constraints.
size -- the total number of “slots” you are trying to fill
Return the solution as a list of values.
"""
solution = [None] * size
def extend_solution(position):
for value in values:
solution[position] = value
if safe_up_to(solution, position):
if position >= size-1 or extend_solution(position+1):
return solution
return None
return extend_solution(0)
Using it in the NEAS problem:
import backtracker
def no_adjacencies(string, up_to_index):
# See if the sequence filled from indices 0 to up_to_index, inclusive, is
# free of any adjancent substrings. We'll have to try all subsequences of
# length 1, 2, 3, up to half the length of the string. Return False as soon
# as we find an equal adjacent pair.
length = up_to_index+1
for j in range(1, length//2+1):
if (string[length-2*j:length-j] == string[length-j:length]):
return False
return True
print(''.join(str(v) for v in backtracker.solve(range(3), no_adjacencies, 50)))
$ python3 neas.py 01020120210120102012021020102101201020120210120102
Using it in the 8-Queens problem:
import backtracker
def no_conflicts(board, up_to_column):
# See if any queens in columns to the left of up_to_column interfere with
# the queen in up_to_column. Return False as soon as you find one that does.
for col in range(up_to_column):
if (board[col] == board[up_to_column] # same row
or board[col] == board[up_to_column] + up_to_column - col # diagonal
or board[col] == board[up_to_column] + col - up_to_column): # diagonal
return False
return True
print(backtracker.solve(range(8), no_conflicts, 8))
$ python3 queens.py [0, 4, 7, 5, 2, 6, 1, 3]