How constraint propagation can be used to reduce the size of the search space of a problem, as illustrated by solving a sudoku puzzle!
Watch the talk here: https://www.youtube.com/watch?v=A_5Hh8xdLFQ
4. def search(values):
if values is False:
return False
if all(len(values[s]) == 1 for s in boxes):
return values
# Choose one of the unfilled squares with the fewest possibilities
n, s = min((len(values[s]), s) for s in boxes if len(values[s]) > 1)
# Now use recurrence to solve each one of the resulting sudokus, and
for value in values[s]:
new_sudoku = values.copy()
new_sudoku[s] = value
attempt = search(new_sudoku)
if attempt:
return attempt
6. Propagate the constraints through the search space to reduce the number of
options and make the problem tractable
Constraint Propagation
7. 7
def eliminate(values):
solved_values = get_solved_values()
for box in solved_values:
digit = values[box]
for peer in peers[box]:
values[peer] = values[peer].replace(digit, '')
return values
8. def only_choice(values):
for unit in unitlist:
for digit in '123456789':
dplaces = [box for box in unit if digit in values[box]]
if len(dplaces) == 1:
values[dplaces[0]] = digit
return values
9. Naked twins
9
def naked_twins(values):
[prune_unit(values, r) for r in column_units]
[prune_unit(values, r) for r in row_units]
[prune_unit(values, r) for r in square_units]
[prune_unit(values, r) for r in diagonal_units]
return values
def prune_unit(values, unit):
two_dig_cells = [d for d in unit if len(values[d]) == 2]
if len(two_dig_cells) > 1:
for p1 in range(0, len(two_dig_cells)):
for p2 in range(p1 + 1, len(two_dig_cells)):
if values[two_dig_cells[p1]] == values[two_dig_cells[p2]]:
for dig in values[two_dig_cells[p1]]:
def remover(c):
values[c] = values[c].replace(dig, '')
for x in unit:
if x not in [two_dig_cells[p1], two_dig_cells[p2]]:
remover(x)
11. 11
def reduce_puzzle(values):
stalled = False
while not stalled:
solved_values_before = len(get_solved_values(values))
eliminate(values)
only_choice(values)
naked_twins(values)
solved_values_after = len(get_solved_values(values))
stalled = solved_values_before == solved_values_after
if len([box for box in values.keys() if len(values[box]) == 0]):
return False
return values
12. def search(values):
values = reduce_puzzle(values)
if values is False:
return False
if all(len(values[s]) == 1 for s in boxes):
return values
# Choose one of the unfilled squares with the fewest possibilities
n, s = min((len(values[s]), s) for s in boxes if len(values[s]) > 1)
# Now use recurrence to solve each one of the resulting sudokus, and
for value in values[s]:
new_sudoku = values.copy()
new_sudoku[s] = value
attempt = search(new_sudoku)
if attempt:
return attempt