Download
/*
N-queens in B-Prolog.
Model created by Hakan Kjellerstrand, hakank@gmail.com
See also my B-Prolog page: http://www.hakank.org/bprolog/
*/
% Licenced under CC-BY-4.0 : http://creativecommons.org/licenses/by/4.0/
/*
More systematic test of queens2/2 and queens/2.
Both queens2/2 and queens/2 take long time for N=500
(but is much faster for N=499 and N=500:
For N=400:
queens2/2: 0.62s 10 backtracks
queens/2: 2.16s 1 backtrack
For N=499:
queens2/2: 0.76 1 backtracks
queens/2: 4.168 0 backtracks
For N=500: too slow
For N=501:
queens2/2: 3.14s 1 backtrack
queens/2: 3.524s 2 backtracks
For N=1000:
queens2/2: 24.2s 2 backtracks
queens/2: 34.146s 0 backtracks
*/
%
% Reporting both time and backtracks
%
time2(Goal):-
cputime(Start),
statistics(backtracks, Backtracks1),
call(Goal),
statistics(backtracks, Backtracks2),
cputime(End),
T is (End-Start)/1000,
Backtracks is Backtracks2 - Backtracks1,
format('CPU time ~w seconds. Backtracks: ~d\n', [T, Backtracks]).
% Decomposition of alldifferent
alldifferent_me(L) :-
length(L, Len),
foreach(I in 1..Len, J in I+1..Len, L[I] #\= L[J]).
%
% Decomposition of alldifferent for ip_solve
% (used in queens7/2).
%
alldifferent_mip(L) :-
length(L, Len),
foreach(I in 1..Len, J in I+1..Len, L[I] $\= L[J]).
%
% Testing correctness of queens/2 (using alldistinct)
%
go :-
N = 8,
findall(Q, queens(N,Q), L),
length(L,Len),
writeln(L),
writeln(len:Len).
% Larger example
go2 :-
N = 300,
time2(queens(N,Q)),
writeln(Q).
% Testing queens2/2.
% This is quite hard. > 35 minutes...
go3 :-
N = 500,
time2(queens2(N,Q)),
writeln(Q).
% Testing queens3/2
go4 :-
N = 8,
time2(queens3(N,Q)),
writeln(Q).
go5 :-
Sizes = [8,10,20,100,200,300,400,499,501],
foreach(N in Sizes,
[Q,Q2],
(
garbage_collect,
writeln(n:N),
% time2(queens5(N, Q)),
% writeln(queens5:Q),
time2(queens(N, Q)),
writeln(queens:Q),
time2(queens2(N, Q2)),
writeln(queens2:Q2),
nl
)
).
% Using the decomposition of alldifferent.
% Testing correctness.
go6 :-
N = 8,
findall(Q, queens4(N,Q), L),
length(L,Len),
writeln(L),
writeln(len:Len).
% Using the decomposition of alldifferent
go7 :-
N = 50,
time2(queens4(N,Q)),
writeln(Q).
%
% Systematic test of queens4/2 (decomposition of alldifferent)
%
go8 :-
foreach(N in 8..5..100,
[Q],
(
writeln(n:N),
time2(queens4(N, Q)),
writeln(Q),
nl
)
).
%
% Using SAT solver (sat_solve)
% Not blazingly fast:
% N=50: 3.18s
% N=100: 25.66s
%
go9 :-
findall(Q, queens6(8,Q), All),
writeln(All),
length(All, Len),
writeln(len:Len),
time2(queens6(100,Q2)),
writeln(Q2).
%
% Using IP solver (ip_solve)
%
go9 :-
time2(queens7(8,Q)),
writeln(Q).
%
% Traditional: using 3 alldistinct.
%
queens(N, Q) :-
length(Q, N),
Q :: 1..N,
% Note: We must "extract" via @=
% This don't_ work: alldistinct([Q[I]+I : I in 1..N])
Q2 @= [Q[I]+I : I in 1..N],
Q3 @= [Q[I]-I : I in 1..N],
alldistinct(Q),
alldistinct(Q2),
alldistinct(Q3),
labeling([ff],Q).
%
% Traditional: Using alldifferent instead of alldistinct
% is little faster than using alldistinct.
%
queens2(N, Q) :-
length(Q, N),
Q :: 1..N,
Q2 @= [Q[I]+I : I in 1..N],
Q3 @= [Q[I]-I : I in 1..N],
alldifferent(Q),
alldifferent(Q2),
alldifferent(Q3),
labeling([ff],Q).
%
% This is - unsurprisingly - much slower.
%
queens3(N, Q) :-
length(Q, N),
Q :: 1..N,
foreach(I in 1..N, J in I+1..N,
(
Q[I] #\= Q[J],
Q[I] + I #\= Q[J],
Q[I] - I #\= Q[J]
;
true
)
),
labeling([ff], Q).
%
% Using decomposition of alldifferent. Slower.
%
queens4(N, Q) :-
length(Q, N),
Q :: 1..N,
Q2 @= [Q[I]+I : I in 1..N],
Q3 @= [Q[I]-I : I in 1..N],
alldifferent_me(Q),
alldifferent_me(Q2),
alldifferent_me(Q3),
labeling([ffd],Q).
%
% slightly different approach (from queens/2 and queens2/2)
% where we start with an array A and then extract the
% lists Q from A.
%
queens5(N, Q) :-
new_array(A,[N]),
% Q @= [A[I] : I in 1..N],
array_to_list(A,Q),
Q :: 1..N,
Q2 @= [A[I]+I : I in 1..N],
Q3 @= [A[I]-I : I in 1..N],
% alldifferent(Q),
% alldifferent(Q2),
% alldifferent(Q3),
alldistinct(Q),
alldistinct(Q2),
alldistinct(Q3),
% labeling([ff],Q).
labeling([ff],Q).
%
% SAT based solution
% Note: This only generate one solution
%
queens6(N, Q) :-
length(Q, N),
Q :: 1..N,
Q2 @= [Q[I]+I : I in 1..N],
Q3 @= [Q[I]-I : I in 1..N],
% Special constraints for SAT solve
$alldifferent(Q),
$alldifferent(Q2),
$alldifferent(Q3),
sat_solve(Q).
%
% LP based solution
% Note: This only generate one solution.
% Time:
% N=8: 2.56s
% N=10: 61.6s
%
queens7(N, Q) :-
length(Q, N),
Q :: 1..N,
foreach(QQ in Q, lp_integer(QQ)),
Q2 @= [Q[I]+I : I in 1..N],
Q3 @= [Q[I]-I : I in 1..N],
% Using $alldifferent/1 don't work
% (GLPK give PROBLEM HAS NO FEASIBLE SOLUTION)
% $alldifferent(Q),
% $alldifferent(Q2),
% $alldifferent(Q3),
% This works, though.
alldifferent_mip(Q),
alldifferent_mip(Q2),
alldifferent_mip(Q3),
ip_solve(Q).
%
% Collecting all the alldifference in a foreach loop.
% However it's slower than queens/2 and queens2/2...
%
queens8(N, Q) :-
length(Q, N),
Q :: 1..N,
foreach(A in [-1,0,1],
[QQ],
(
QQ @= [Q[I]+I*A : I in 1..N],
alldifferent(QQ)
)
),
labeling([ffd],Q).