与任何编程语言一样,要解决Prolog的问题,无论是声明性的还是命令性的,都必须考虑解决方案和输入的表示形式。
由于这是一个编程问题,因此它在StackOverflow.com上很流行,程序员可以在其中解决编程问题。在这里,我将尝试变得更加科学。
为了解决OP中的问题,必须逆转输入中所述依赖项所定义的关系。形式为A t t e n d (X )→ A t t的子句易于逆转。条款甲吨吨ë Ñ d (甲d )∧ 甲吨吨ë Ñ d (甲吨吨é Ñ d(X)→甲吨吨é Ñ d(是)∧ 甲吨吨é Ñ d(Z)像甲吨吨é Ñd(甲d )∧ 甲吨吨é Ñ d(B M)→ 甲吨吨é Ñd(D D )
黛西·多德里奇(Daisy Dodderidge)表示,如果阿不思·邓布利多(Albus Dumbledore)和Burdock Muldoon都来,她会来的
更难治疗。
使用Prolog,第一种简单的方法是避免关系的完全逆转,而要以目标为导向。
假设客人名单上的订单并使用规则
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪A (X)∧ 甲(ÿ)一(w ^)一(w ^)Xÿ→ A (Z),→ A (X),→ A (Y),< Z,< Z⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⊢一(w ^)→ A (Z)
(为了简短起见,我们使用而不是A t t e n d (X ))A (X)甲吨吨é Ñ d(X)
该规则易于实施。
一个相当幼稚的方法
为了提高可读性follows
,brings
将其作为输入给出的关系,并将其取反。
然后输入由
follows(bm,[ad]).
follows(cp,[ad]).
follows(ad,[cp]).
follows(dd,[cp]).
follows(ad,[ec]).
follows(bm,[ec]).
follows(cp,[ec]).
follows(cp,[fa]).
follows(dd,[fa]).
follows(bm,[cp,dd]).
follows(ec,[cp,dd]).
follows(fa,[cp,dd]).
follows(dd,[ad,bm]).
并且brings
可以定义如下:
brings(X,S):-brings(X,S,[]).
brings(_X,[],_S).
brings(X,[X|L],S):-brings(X,L,[X|S]).
brings(X,[Y|L],S):-follows(Y,[X]),brings(X,L,[Y|S]).
brings(X,[Y|L],S):-follows(Y,[A,B]),
member(A,S),member(B,S),brings(X,L,[Y|S]).
brings/3(X,L,S)
X
如果我们定义
partymaker(X):-Guests=[ad,bm,cp,dd,ec,fa],member(X,Guests),brings(X,Guests).
我们得到以下独特的解决方案:
[ad,ec]
这不是完整的列表,因为根据字母顺序,该子句
follows(bm,[cp,dd]).
不管用。
解决原始难题的相当复杂的方法
为了完全解决该问题,您实际上必须让系统尝试为以后的访客证明出勤率,而不会在搜索树中引入无限循环。有多种方法可以实现此目标。每种都有其优点和缺点。
一种方法是重新定义brings/2
如下:
brings(X,S):-brings(X,S,[],[]).
% brings(X,RemainsToBring,AlreadyTaken,AlreadyTried).
%
% Problem solved
brings(_X,[],_S,_N).
% Self
brings(X,[X|L],S,N):-brings(X,L,[X|S],N).
% Follower
brings(X,[Y|L],S,N):-follows(Y,[X]),brings(X,L,[Y|S],N).
% Y is not a follower, but X can bring 2
brings(X,[Y|L],S,N):- \+member(Y,N),\+follows(Y,[X]),
follows(Y,[A,B]),
try_bring(X,A,L,S,[Y|N]),
try_bring(X,B,L,S,[Y|N]),brings(X,L,[Y|S],N).
% Y is not a follower, but X can bring 1
brings(X,[Y|L],S,N):- \+member(Y,N),\+follows(Y,[X]),\+follows(Y,[_A,_B]),
follows(Y,[C]),
try_bring(X,C,L,S,[Y|N]),brings(X,L,[Y|S],N).
try_bring(_X,A,_L,S,_N):-member(A,S).
try_bring(X,A,L,S,N):- \+member(A,S),sort([A|L],Y),brings(X,Y,S,N).
in中的最后一个参数brings/4
对于避免in中的无限循环是必需的try_bring
。
这给出了以下答案:Albus,Carlotta,Elfrida和Falco。但是,此解决方案并不是最有效的解决方案,因为引入了回溯,有时可以避免回溯。
通用解决方案
在将第三次国际NoCOUG SQL和NoSQL挑战赛的链接添加到原始问题之后,很明显,我们所追求的是对来宾集的子集的通用可达性检查器,其中过渡关系由给出的规则使得规则r的应用[R (X,S):V→ V′
如果小号⊆ VV′= V∪ { X}
我们对最小子集感兴趣 VüV
add_element(X,V,U):- ( var(V) -> % set difference that works in both modes
member(X,U),subtract(U,[X],V);
\+member(X,V),sort([X|V],U) ).
support(V,U):- guests(G), % rule application
member(X,G),
add_element(X,V,U),
follows(X,S),
subset(S,V).
set_support(U,V):- support(V1,U), % sort of a minimal set
( support(_V2,V1) ->
set_support(V1,V) ;
V = V1).
is_duplicate(X,[Y|L]):- ( subset(Y,X) ; is_duplicate(X,L) ).
% purging solutions that are not truly minimal
minimal_support(U,L):-minimal_support(U,[],L).
minimal_support([],L,L).
minimal_support([X|L],L1,L2):-( append(L,L1,U),is_duplicate(X,U) ->
minimal_support(L,L1,L2);
minimal_support(L,[X|L1],L2) ).
solution(L):- guests(G),setof(X,set_support(G,X),S),
minimal_support(S,L).
现在,例如,给定数据集#2为
follows(fa,[dd,ec]).
follows(cp,[ad,bm]).
guests([ad,bm,cp,dd,ec,fa]).
我们得到答案L = [[ad,bm,dd,ec]]。这意味着除卡洛特和法尔科之外,所有客人都必须被邀请。
该解决方案给我的答案与Wicked Witch文章中给出的解决方案相匹配,但数据集#6除外,在该数据集中产生了更多的解决方案。这似乎是正确的解决方案。
最后,我必须提到Prolog的CLP(FD)库,它特别适合此类问题。
attend(BM) :- attend(AD).
与attend(X) :- attend(Y).