我正在尝试解决一个CSP问题,我需要在调酒师身上分发鸡尾酒,这样每个调酒师最多只能喝一杯鸡尾酒,所有的鸡尾酒都会给酒保。我通过创建一个clpfd变量列表来解决它,首先给它们所有调酒师的完整域名,然后删除所有不知道如何制作鸡尾酒的调酒师。
我的代码有效,但有一个问题:它太慢了。如果我查看分析器,remove_domain会被调用2000次(对于输入我正在给我的程序),而它的重做统计数据是> 100 000。
我需要更改其中一个函数(或两者),以便prolog不需要回溯?
produce_domains(_,_,[],[]) :- !.
produce_domains(Bartenders,NBartenders,[Cocktail|Cocktails],[Var|Vars]) :-
Var in 1..NBartenders,
remove_domain(Bartenders,NBartenders,Cocktail,Var),!,
produce_domains(Bartenders,NBartenders,Cocktails,Vars),!.
remove_domain([],0,_,_) :- !.
remove_domain([Bartender|Bartenders],NBartenders,Cocktail,Var) :-
(\+ member(Cocktail,Bartender) -> Var #\= NBartenders;!),!,
NNBartenders is NBartenders - 1,
remove_domain(Bartenders,NNBartenders,Cocktail,Var),!.
我已经阅读了this related question,但我使用的是SWI-Prolog(5.10.5)的最新Windows版本,所以这不应该是问题。
答案 0 :(得分:2)
您不需要这么多!/0
:Prolog通常可以说您的谓词是确定性的。
首先让我提供您的代码的以下版本。它使用更具关系性的名称,不包含!/0
,并使用更高阶的谓词来缩短代码。
:- use_module(library(clpfd)).
bartenders_cocktails_variables(Bs, Cs, Vs) :-
length(Bs, LBs),
maplist(bartenders_cocktail_variable(Bs, LBs), Cs, Vs).
bartenders_cocktail_variable(Bs, N, C, V) :-
V in 1..N,
foldl(compatible_bartender(C,V), Bs, 1, _).
compatible_bartender(C, V, Cs, N0, N1) :-
( member(C, Cs) -> true
; V #\= N0
),
N1 #= N0 + 1.
请注意,我正在向上计数而不是向下计算调酒师(这只是他们能够混合的鸡尾酒列表),因为这似乎更自然。我还可以通过简单地切换if-then-else的分支来省略(\+)/1
。
示例查询,显示谓词在此用例中是确定性的:
?- bartenders_cocktails_variables([[a,b],[a,b],[x,y]], [x,a,b], Vars).
Vars = [3, _G1098, _G1101],
_G1098 in 1..2,
_G1101 in 1..2.
我们看到:鸡尾酒x
必须由第三个调酒师等混合。
我认为你的程序的这一部分可能不对你所描述的缓慢性能负责。也许你的程序的其他部分(无意中)不是确定性的?也许尝试不同的标签策略或其他约束?如果您发布更多背景信息,我们可能会为您提供更多帮助。