Per risolvere un problema con Prolog, come con qualsiasi linguaggio di programmazione, sia esso dichiarativo o imperativo, devi pensare alla rappresentazione della soluzione e dell'input.
Poiché questa è una domanda di programmazione, sarebbe stata popolare su StackOverflow.com dove i programmatori risolvono problemi di programmazione. Qui proverei a essere più scientifico.
Per risolvere il problema nel PO si deve invertire la relazione definita dalle dipendenze indicate nell'input. Le clausole del modulo sono facili da invertire. Le clausole A t t e n d ( A D ) ∧ A t t e n d (Attend(X)→Attend(Y)∧Attend(Z) comeUNttend( A D ) ∧ Attend(BM) → A ttend( D D)
Daisy Dodderidge ha detto che sarebbe venuta se fossero venuti entrambi Albus Silente e Burdock Muldoon
sono più difficili da trattare.
Con Prolog il primo approccio semplice è quello di evitare una completa inversione della relazione e di essere invece diretto all'obiettivo.
Supponi un ordine nell'elenco degli ospiti e usa una regola
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪UN ( X) ∧ A ( Y)UN ( W)UN ( W)XY→ A ( Z),→ A ( X),→ A ( Y) ,< Z,< Z⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⊢A ( W) → A ( Z)
(Usiamo invece di A t t e n d ( X ) per mantenerlo breve)A ( X)A t t e n d( X)
Questa regola è facile da implementare.
Un approccio piuttosto ingenuo
Per leggibilità, follows
sia la relazione data come input, e brings
sii il suo contrario.
Quindi l'input è dato da
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]).
E brings
può essere definito come segue:
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
Se definiamo
partymaker(X):-Guests=[ad,bm,cp,dd,ec,fa],member(X,Guests),brings(X,Guests).
Otteniamo le seguenti soluzioni uniche:
[ad,ec]
Questo non è l'elenco completo, poiché sotto l'ordine alfabetico la clausola
follows(bm,[cp,dd]).
non funziona.
Una soluzione piuttosto complicata al puzzle originale
Per risolvere completamente il problema devi effettivamente consentire al sistema di provare a provare la presenza per gli ospiti successivi senza introdurre loop infiniti nella struttura di ricerca. Esistono diversi modi per raggiungere questo obiettivo. Ognuno ha i suoi vantaggi e svantaggi.
Un modo è ridefinire brings/2
come segue:
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).
L'ultimo argomento in brings/4
è necessario per evitare un loop infinito in try_bring
.
Questo dà le seguenti risposte: Albus, Carlotta, Elfrida e Falco. Tuttavia, questa soluzione non è la più efficiente poiché viene introdotto il backtracking in cui a volte può essere evitato.
Una soluzione generale
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).
Ora se per esempio il set di dati n. 2 viene dato come
follows(fa,[dd,ec]).
follows(cp,[ad,bm]).
guests([ad,bm,cp,dd,ec,fa]).
Otteniamo la risposta L = [[ad, bm, dd, ec]]. Ciò significa che tutti gli ospiti tranne Carlotte e Falco devono essere invitati.
Le risposte che questa soluzione mi ha dato corrispondevano alle soluzioni fornite nell'articolo Wicked Witch con l'eccezione del set di dati n. 6, dove venivano prodotte più soluzioni. Questa sembra essere la soluzione corretta.
Infine, devo citare la libreria CLP (FD) di Prolog, particolarmente adatta a questo tipo di problemi.
attend(BM) :- attend(AD).
è esattamente lo stesso diattend(X) :- attend(Y).