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]