5 ms·
The declarative programming language Prolog is a natural choice for solving such combinatorial tasks. Here is a Prolog formulation of the puzzle, using constra
by zmonx 9y ago
The declarative programming language Prolog is a natural choice for solving such combinatorial tasks.
Here is a Prolog formulation of the puzzle, using constraint logic programming over integers that ships with typical Prolog systems:
binary_puzzle(Rows) :-
length(Rows, L),
maplist(same_length(Rows), Rows),
maplist(only_two_next_to_each_other, Rows),
transpose(Rows, Cols),
maplist(only_two_next_to_each_other, Cols),
maplist(booleans_integer, Rows, RIs),
all_different(RIs),
maplist(booleans_integer, Cols, CIs),
all_different(CIs),
Half #= L // 2,
maplist(equally_distributed(Half), Rows),
maplist(equally_distributed(Half), Cols).
equally_distributed(L, Bs) :-
global_cardinality(Bs, [0-L,1-L]).
booleans_integer(Bs, I) :-
foldl(pow, Bs, 0-0, I-_).
pow(B, N0-I0, N-I) :-
B in 0..1,
N #= N0 + B*2^I0,
I #= I0 + 1.
only_two_next_to_each_other([]).
only_two_next_to_each_other([_,_]).
only_two_next_to_each_other([A,B,C|Rest]) :-
( A #= B ) #==> ( C #\= B ),
( B #= C ) #==> ( B #\= A ),
only_two_next_to_each_other([B,C|Rest]).
For example, let us consider the concrete 10x10 puzzle of today:
puzzle([[_,_,_,_,_,1,_,_,_,1],
[1,_,_,_,_,_,_,0,_,_],
[_,_,0,_,_,_,_,0,_,_],
[_,0,0,_,_,_,0,_,_,1],
[1,_,_,_,_,_,_,_,_,1],
[_,_,_,0,_,_,1,_,_,_],
[0,_,_,_,_,1,_,_,_,_],
[_,_,_,_,_,_,_,0,_,0],
[0,_,_,_,_,_,_,_,_,0],
[_,0,_,0,_,1,_,_,_,_]]).
You can solve it with the above formulation, using for example SICStus Prolog and its CLP(FD) library, with the following query:
?- puzzle(Rows),
binary_puzzle(Rows),
maplist(label, Rows),
maplist(portray_clause, Rows).
The unique solution is:
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1].
[1, 0, 1, 0, 0, 1, 1, 0, 1, 0].
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0].
[1, 0, 0, 1, 1, 0, 0, 1, 0, 1].
[1, 0, 1, 0, 0, 1, 0, 0, 1, 1].
[0, 1, 1, 0, 1, 0, 1, 1, 0, 0].
[0, 1, 0, 1, 0, 1, 0, 0, 1, 1].
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0].
[0, 1, 0, 1, 1, 0, 1, 1, 0, 0].
[1, 0, 1, 0, 0, 1, 0, 1, 0, 1].
It is found within a second on current machines.
The above formulation is also quite general. For example, you can use it to complete, test and generate solutions:
?- binary_puzzle(Rows),
maplist(label, Rows),
maplist(portray_clause, Rows).
This generates valid solutions of the puzzle for all board sizes:
Rows = [] ;
[0, 1].
[1, 0].
Rows = [[0, 1], [1, 0]] ;
[1, 0].
[0, 1].
Rows = [[1, 0], [0, 1]] ;
[0, 0, 1, 1].
[0, 1, 0, 1].
[1, 0, 1, 0].
[1, 1, 0, 0].
Rows = [[0, 0, 1, 1], [0, 1, 0, 1], [1, 0, 1, 0], [1, 1, 0, 0]] ;
[0, 0, 1, 1].
[0, 1, 0, 1].
[1, 1, 0, 0].
[1, 0, 1, 0].
Rows = [[0, 0, 1, 1], [0, 1, 0, 1], [1, 1, 0, 0], [1, 0, 1, 0]] ;
etc.
Thank you for sharing!
- bluetwo 9y agoI'm curious how long it would take to solve a hard puzzle using this technique. Anyone know?
- eriknstr 9y agoA hard variant of binary puzzle, or a puzzle that is different from the binary puzzle and is also hard? If the former, the 10x10 puzzle for today (solved in parent comment) is classified as "very hard".
- zmonx 9y agoAs eriknstr correctly points out, the puzzle I have included above is already classified as "very hard". SICStus Prolog 4.3.2 takes about 20 milliseconds to find the solution with the formulation I posted. I tested this on a machine where cat /dev/cpuinfo yields: vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz stepping : 5 microcode : 0x11 cpu MHz : 2668.000 cache size : 8192 KB Prolog is very well suited for solving such combinatorial tasks, and the powerful pruning algorithms of constraint-based solutions using Prolog will often outperform other formulations of such problems. I have used the following Prolog code to benchmark the running time of a goal: goal_time(Goal, Time) :- statistics(runtime, [T0,_]), ( catch(Goal, error(resource_error(memory), _), E = memory) -> true ; functor(Goal, F, A), portray_clause(failed(F/A)) ), statistics(runtime, [T1,_]), T #= T1 - T0, ( nonvar(E) -> Time = exception(E, T) ; Time = time(T) ). For example, it takes about 1 second (990 milliseconds) to count from 1 to 10 million: ?- goal_time(ignore((between(1,10000000,_),false)), T). T = time(990).
- thomasahle 9y agoDid you try graphing how runtime scales as a function of puzzle size? (Assuming there are puzzles available of large sizes.) It would also be cool if your program could be used to generate new puzzles?