|
In the solution of this exercise, 4 example puzzles are given. Here is the first one: knuth(a, [[1,_,_,_,_,6,_,8,_],
[5,_,8,7,2,1,4,_,6],
[_,6,_,3,8,_,2,_,1],
[8,4,_,_,_,3,_,_,5],
[_,_,5,_,6,_,8,_,_],
[6,_,_,8,_,_,_,4,2],
[3,_,6,_,4,8,_,2,_],
[4,_,7,6,3,2,1,_,8],
[_,8,_,5,_,_,_,_,4]]).
Consider now a Prolog solution for 9x9 (i.e., "normal") Sudoku puzzles, using integer constraints: sudoku(Rows) :-
length(Rows, 9), maplist(same_length(Rows), Rows),
append(Rows, Vs), Vs ins 1..9,
maplist(all_distinct, Rows),
transpose(Rows, Columns), maplist(all_distinct, Columns),
Rows = [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is],
blocks(As, Bs, Cs), blocks(Ds, Es, Fs), blocks(Gs, Hs, Is).
blocks([], [], []).
blocks([N1,N2,N3|Ns1], [N4,N5,N6|Ns2], [N7,N8,N9|Ns3]) :-
all_distinct([N1,N2,N3,N4,N5,N6,N7,N8,N9]),
blocks(Ns1, Ns2, Ns3).
This is indeed exactly a case where search is necessary, because the pruning applied by this concrete constraint solver is not strong enough to deduce the unique solution. If we only post the constraints, we get: ?- knuth(a, Rows),
sudoku(Rows),
maplist(portray_clause, Rows).
[1, _, _, _, _, 6, _, 8, _].
[5, _, 8, 7, 2, 1, 4, _, 6].
[_, 6, _, 3, 8, _, 2, _, 1].
[8, 4, _, _, _, 3, _, _, 5].
[_, _, 5, _, 6, _, 8, _, _].
[6, _, _, 8, _, _, _, 4, 2].
[3, _, 6, _, 4, 8, _, 2, _].
[4, _, 7, 6, 3, 2, 1, _, 8].
[_, 8, _, 5, _, _, _, _, 4].
However, it is exactly as you say: Even assigning a single variable a concrete value automatically enforces the solution by constraint propagation alone: ?- knuth(a, Rows),
sudoku(Rows),
Rows = [[_,Var|_]|_],
indomain(Var),
maplist(portray_clause, Rows).
[1, 2, 3, 4, 5, 6, 7, 8, 9].
[5, 9, 8, 7, 2, 1, 4, 3, 6].
[7, 6, 4, 3, 8, 9, 2, 5, 1].
[8, 4, 2, 1, 9, 3, 6, 7, 5].
[9, 7, 5, 2, 6, 4, 8, 1, 3].
[6, 3, 1, 8, 7, 5, 9, 4, 2].
[3, 1, 6, 9, 4, 8, 5, 2, 7].
[4, 5, 7, 6, 3, 2, 1, 9, 8].
[2, 8, 9, 5, 1, 7, 3, 6, 4].
Note though the general point: A sufficiently strong constraint solver could have deduced the unique solution even without trying any concrete value, exclusively by reasoning about the posted constraints with sufficient sophistication! This concrete solver can't, due to the particular trade-off between efficiency and pruning strength that was chosen by the implementor, and so search must settle the remaining part.One of the other puzzles is: knuth(d, [[1,_,3,_,5,6,_,8,9],
[6,8,_,3,_,9,1,_,5],
[_,9,5,1,8,_,6,3,_],
[3,_,8,9,6,_,_,5,1],
[_,1,9,5,_,8,3,6,_],
[5,6,_,_,3,1,9,_,8],
[_,5,6,_,9,3,8,1,_],
[8,_,1,6,_,5,_,9,3],
[9,3,_,8,1,_,5,_,6]]).
In this case, the solution is not unique. We can use the constraint solver to count the number of admissible solutions, again using search: ?- knuth(d, Rows),
sudoku(Rows),
findall(., maplist(labeling([ff]), Rows), Ls),
length(Ls, L).
Yielding: L = 12.This indeed shows that search is necessary in general also when applying constraint programming. Still, no such search is applied in the posted article. In the example contained in the article, the constraints alone suffice to determine the unique solution. I leave it as an exercise for the reader to determine whether this is the case for all 4x4 Sudoku puzzles. |
I'm curious on how you claim a strong enough solver could have solved this without a "guess."
Also, any thoughts on how treating them as constraint propagation compares to casting it as an exact cover problem?