Prologで問題を解決するには、他のプログラミング言語と同様に、宣言型であれ、命令型であれ、ソリューションの表現と入力について考える必要があります。
これはプログラミングの質問なので、プログラマがプログラミングの問題を解決するStackOverflow.comで人気があったでしょう。ここで、私はもっと科学的になろうとします。
Attend(X)→Attend(Y)∧Attend(Z)Attend(AD)∧Attend(BM)→Attend(DD)
デイジー・ドッデリッジは、アルバス・ダンブルドアとゴボウ・マルドゥーンの両方が来たら彼女が来ると言った
治療がより困難です。
Prologを使用する最初の簡単なアプローチは、関係の完全な逆転を回避し、代わりに目標を指示することです。
ゲストのリストで注文を想定し、ルールを使用する
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪A(X)∧A(Y)A(W)A(W)XY→A(Z),→A(X),→A(Y),<Z,<Z⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⊢A(W)→A(Z)
A(X)Attend(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]).
動かない。
元のパズルのかなり複雑なソリューション
問題を完全に解決するには、検索ツリーに無限ループを導入することなく、実際にシステムに後のゲストの出席を証明させなければなりません。この目標を達成する方法は複数あります。それぞれに長所と短所があります。
1つの方法は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).
の最後の引数brings/4
は、の無限ループを回避するために必要try_bring
です。
これにより、Albus、Carlotta、Elfrida、Falcoの回答が得られます。ただし、このソリューションは、バックトラックが時々回避できる場所に導入されるため、最も効率的なソリューションではありません。
一般的な解決策
r(X,S):V→V′
S⊆VV′=V∪{X}
VUV
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]]になります。つまり、CarlotteとFalco以外のすべてのゲストを招待する必要があります。
このソリューションが私に与えた答えは、より多くのソリューションが作成されたデータセット#6を除き、Wicked Witchの記事で示されたソリューションと一致しました。これが正しい解決策のようです。
最後に、この種の問題に特に適したPrologのCLP(FD)ライブラリについて言及する必要があります。
attend(BM) :- attend(AD).
と全く同じであるattend(X) :- attend(Y).