Introduction
In Prolog, CLP (FD) constraints are the right choice for such planning tasks.
See clpfd for more information.
In this case, I suggest using the powerful global_cardinality/2 restriction to limit the number of entries of each depending on the number of vessels available. We can use an iterative recess to find the minimum number of allowable rounds.
The freely available Prolog systems are sufficient to solve the problem satisfactorily. Commercial systems will work ten times faster.
Option 1: Solution Using SWI-Prolog
:- use_module(library(clpfd)). tennis(N, Courts, Rows) :- length(Rows, N), maplist(same_length(Rows), Rows), transpose(Rows, Rows), Rows = [[_|First]|_], chain(First, #<), length(_, MaxRounds), numlist(1, MaxRounds, Rounds), pairs_keys_values(Pairs, Rounds, Counts), Counts ins 0..Courts, foldl(triangle, Rows, Vss, Dss, 0, _), append(Vss, Vs), global_cardinality(Vs, Pairs), maplist(breaks, Dss), labeling([ff], Vs). triangle(Row, Vs, Ds, N0, N) :- length(Prefix, N0), append(Prefix, [-|Vs], Row), append(Prefix, Vs, Ds), N #= N0 + 1. breaks([]). breaks([P|Ps]) :- maplist(breaks_(P), Ps), breaks(Ps). breaks_(P0, P) :- abs(P0-P) #> 1.
Example request: 5 players on two ships:
?- time(tennis(5, 2, Rows)), maplist(writeln, Rows). % 827,838 inferences, 0.257 CPU in 0.270 seconds (95% CPU, 3223518 Lips) [-,1,3,5,7] [1,-,5,7,9] [3,5,-,9,1] [5,7,9,-,3] [7,9,1,3,-]
The specified problem, 6 players on 2 ships , is well solved within 1 minute:
? - time (tennis (6, 2, Rows)),
maplist (format ("~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + \ n" ), Rows).
% 6,675,665 inferences, 0.970 CPU in 0.977 seconds (99% CPU, 6884940 Lips)
- 1 3 5 7 10
1 - 6 9 11 3
3 6 - 11 9 1
5 9 11 - 2 7
7 11 9 2 - 5
10 3 1 7 5 -
Further example: 7 players on 5 ships:
? - time (tennis (7, 5, Rows)),
maplist (format ("~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + \ n "), Rows).
% 125,581,090 inferences, 17.476 CPU in 18.208 seconds (96% CPU, 7185927 Lips)
- 1 3 5 7 9 11
1 - 5 3 11 13 9
3 5 - 9 1 7 13
5 3 9 - 13 11 7
7 11 1 13 - 5 3
9 13 7 11 5 - 1
11 9 13 7 3 1 -
Option 2: Solution with SICStus Prolog
With the following additional compatibility definitions, the same program also runs in SICStus Prolog:
: - use_module (library (lists)).
: - use_module (library (between)).
: - op (700, xfx, ins).
Vs ins D: - maplist (in_ (D), Vs).
in_ (D, V): - V in D.
chain ([], _).
chain ([L | Ls], Pred): -
chain_ (Ls, L, Pred).
chain _ ([], _, _).
chain _ ([L | Ls], Prev, Pred): -
call (Pred, Prev, L),
chain_ (Ls, L, Pred).
pairs_keys_values โโ(Ps, Ks, Vs): - keys_and_values โโ(Ps, Ks, Vs).
foldl (Pred, Ls1, Ls2, Ls3, S0, S): -
foldl_ (Ls1, Ls2, Ls3, Pred, S0, S).
foldl _ ([], [], [], _, S, S).
foldl _ ([L1 | Ls1], [L2 | Ls2], [L3 | Ls3], Pred, S0, S): -
call (Pred, L1, L2, L3, S0, S1),
foldl_ (Ls1, Ls2, Ls3, Pred, S1, S).
time (Goal): -
statistics (runtime, [T0 | _]),
call (Goal),
statistics (runtime, [T1 | _]),
T # = T1 - T0,
format ("% Runtime: ~ Dms \ n", [T]).
The main difference: SICStus, which is a commercial Prolog that comes with a serious CLP (FD) system, is much faster than SWI-Prolog in this case and others like it.
The specified task, 6 players on 2 ships:
? - time (tennis (6, 2, Rows)),
maplist (format ("~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + \ n" ), Rows).
% Runtime: 34ms (!)
- 1 3 5 7 10
1 - 6 11 9 3
3 6 - 9 11 1
5 11 9 - 2 7
7 9 11 2 - 5
10 3 1 7 5 -
Example:
| ? - time (tennis (7, 5, Rows)),
maplist (format ("~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + ~ t ~ w ~ 3 + \ n "), Rows).
% Runtime: 884ms
- 1 3 5 7 9 11
1 - 5 3 9 7 13
3 5 - 1 11 13 7
5 3 1 - 13 11 9
7 9 11 13 - 3 1
9 7 13 11 3 - 5
11 13 7 9 1 5 -
Concluding observations
In both systems, global_cardinality/3 allows you to specify parameters that change the propagation power of the global power constraint, which allows weaker and potentially more efficient filtering. Choosing the right options for a particular example can have even greater effect than choosing a Prolog system.