Aby rozwiązać problem z Prologiem, jak w każdym języku programowania, zarówno deklaratywnym, jak i imperatywnym, musisz pomyśleć o reprezentacji rozwiązania i danych wejściowych.
Ponieważ jest to pytanie programistyczne, byłoby popularne na StackOverflow.com, gdzie programiści rozwiązują problemy z programowaniem. Tutaj chciałbym być bardziej naukowy.
Aby rozwiązać problem w PO, należy odwrócić relację określoną przez zależności określone na wejściu. Klauzule postaci można łatwo odwrócić. Klauzule A t t e n d ( A D ) ∧ A t t e n d (Attend(X)→Attend(Y)∧Attend(Z) jakA t t e nd( A D ) ∧ A t t e n d( B M) → A t t e n d( D D )
Daisy Dodderidge powiedziała, że przyjdzie, jeśli przyjdą Albus Dumbledore i Burdock Muldoon
są trudniejsze do leczenia.
W przypadku Prologu pierwsze proste podejście polega na unikaniu pełnego odwrócenia relacji i kierowaniu się celem.
Załóż zamówienie na liście gości i zastosuj regułę
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪A (X) ∧ A ( Y)A (W)A (W)XY→ A ( Z) ,→ A ( X) ,→ A ( Y) ,< Z,< Z⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⊢A ( W) → A ( Z)
(Używamy zamiast A t t e n d ( X ), aby było krótkie)A ( X)A t t e n d( X)
Ta zasada jest łatwa do wdrożenia.
Raczej naiwne podejście
Dla czytelności niech follows
będzie relacją podaną jako dane wejściowe i brings
odwrotnie.
Następnie dane wejściowe są podawane przez
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]).
I brings
można zdefiniować w następujący sposób:
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
Jeśli zdefiniujemy
partymaker(X):-Guests=[ad,bm,cp,dd,ec,fa],member(X,Guests),brings(X,Guests).
Otrzymujemy następujące unikalne rozwiązania:
[ad,ec]
To nie jest pełna lista, ponieważ w kolejności alfabetycznej klauzula
follows(bm,[cp,dd]).
nie działa.
Raczej zaangażowane rozwiązanie oryginalnej układanki
Aby całkowicie rozwiązać problem, musisz faktycznie pozwolić systemowi udowodnić obecność późniejszych gości bez wprowadzania nieskończonych pętli do drzewa wyszukiwania. Istnieje wiele sposobów osiągnięcia tego celu. Każdy ma swoje zalety i wady.
Jednym ze sposobów jest przedefiniowanie brings/2
w następujący sposób:
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).
Ostatni argument brings/4
jest niezbędny, aby uniknąć nieskończonej pętli try_bring
.
To daje następujące odpowiedzi: Albus, Carlotta, Elfrida i Falco. Jednak to rozwiązanie nie jest najskuteczniejsze, ponieważ wycofywanie jest wprowadzane tam, gdzie czasem można go uniknąć.
Ogólne rozwiązanie
Po dodaniu linku do Trzeciego Międzynarodowego Wyzwania NoCOUG SQL i NoSQL w pierwotnym pytaniu stało się jasne, że szukamy ogólnego sprawdzania osiągalności na zbiorze podzbiorów zbioru gości, gdzie relacja przejścia jest zdefiniowana przez zasady podane tak, że zastosowanie reguły r ( X, S) : V→ V.′
S.⊆ V.V.′= V.∪ { X}
V.UV.
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).
Teraz, jeśli na przykład zestaw danych nr 2 jest podany jako
follows(fa,[dd,ec]).
follows(cp,[ad,bm]).
guests([ad,bm,cp,dd,ec,fa]).
Otrzymujemy odpowiedź L = [[ad, bm, dd, ec]]. Co oznacza, że wszyscy goście oprócz Carlotte i Falco muszą zostać zaproszeni.
Odpowiedzi, które dało mi to rozwiązanie, pasowały do rozwiązań podanych w artykule Wicked Witch, z wyjątkiem zestawu danych nr 6, w którym wyprodukowano więcej rozwiązań. To wydaje się właściwe rozwiązanie.
Na koniec muszę wspomnieć o bibliotece CLP (FD) firmy Prolog, która jest szczególnie odpowiednia do tego rodzaju problemów.
attend(BM) :- attend(AD).
jest dokładnie taki sam jakattend(X) :- attend(Y).