前言
在Prolog中,CLP(FD)约束是解决此类调度任务的正确选择。
更多信息请参见clpfd。
在这种情况下,建议使用强大的global_cardinality/2
约束来限制每轮比赛的出现次数,具体取决于可用场地的数量。我们可以使用迭代加深来找到最小可接受轮数。
免费的Prolog系统足以满意地解决此任务。商业级系统将运行几十倍快。
变种1:使用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.
示例查询:2个场地上的5名球员:
?- 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,-]
指定任务为:6名球员在2个场地上进行比赛,程序在1分钟内解决了问题;
进一步的例子是:7名球员在5个场地上比赛。下面是使用SICStus Prolog的解决方案,它比SWI-Prolog快得多。两个系统都可以使用global_cardinality/3约束来实现更高效的过滤。选择针对特定示例的正确选项可能会比选择Prolog系统有更大的影响。