前言
在 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,-]
指定任务,2场6人,1分钟内很好解决:
?- 时间(网球(6, 2, Rows)),
maplist(format("~t~w~3+~t~w~3+~t~w~3+~t~w~3+~t~w~3+~t~w~3+\n" ),行)。
% 6,675,665 推理,0.970 CPU 在 0.977 秒内(99% CPU,6884940 唇)
- 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 -
进一步的例子: 5 个球场上的 7 名球员:
?- 时间(网球(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"), 行)。
% 125,581,090 次推理,17.476 CPU 在 18.208 秒内(96% CPU,7185927 唇)
- 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 -
变体 2:使用 SICStus Prolog 的解决方案
通过以下附加的兼容性定义,相同的程序也可以在 SICStus Prolog 中运行:
:- 使用模块(库(列表))。
:- 使用模块(库(之间))。
:- op(700, xfx, ins)。
Vs ins D :- maplist(in_(D), Vs)。
in_(D, V) :- D 中的 V。
链([], _)。
链([L|Ls],预测):-
链_(Ls,L,Pred)。
链_([], _, _)。
chain_([L|Ls], Prev, Pred) :-
调用(上一个,上一个,L),
链_(Ls,L,Pred)。
pair_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) :-
呼叫(Pred,L1,L2,L3,S0,S1),
foldl_(Ls1, Ls2, Ls3, Pred, S1, S)。
时间(目标):-
统计数据(运行时,[T0|_]),
呼叫(目标),
统计数据(运行时,[T1|_]),
T #= T1 - T0,
格式(“%运行时间:~Dms\n”,[T])。
主要区别:SICStus 是一个商业级 Prolog,带有一个严肃的 CLP(FD) 系统,在这个用例和其他类似的用例中比 SWI-Prolog快得多。
指定任务,2场6名队员:
?- 时间(网球(6, 2, Rows)),
maplist(format("~t~w~3+~t~w~3+~t~w~3+~t~w~3+~t~w~3+~t~w~3+\n" ),行)。
%运行时间:34 毫秒(!)
- 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 -
更大的例子:
| ?- 时间(网球(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"), 行)。
%运行时间:884 毫秒
- 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 -
结束语
在这两个系统中,global_cardinality/3
允许您指定更改全局基数约束的传播强度的选项,从而实现更弱且可能更有效的过滤。为特定示例选择正确的选项可能比选择 Prolog 系统产生更大的影响。