Para resolver un problema con Prolog, como con cualquier lenguaje de programación, ya sea declarativo o imperativo, debe pensar en la representación de la solución y la entrada.
Como se trata de una pregunta de programación, habría sido popular en StackOverflow.com, donde los programadores resuelven problemas de programación. Aquí intentaría ser más científico.
Para resolver el problema en el OP uno tiene que revertir la relación definida por las dependencias establecidas en la entrada. Las cláusulas de la forma son fáciles de revertir. Las cláusulas A t t e n d ( A D ) ∧ A t t e n d (Attend(X)→Attend(Y)∧Attend(Z) comoA t t e n d( A D ) ∧ A t t e n d( B M) → A t t e n d( D D )
Daisy Dodderidge dijo que vendría si Albus Dumbledore y Burdock Muldoon vinieran
son más difíciles de tratar
Con Prolog, el primer enfoque simple es evitar una inversión completa de la relación y, en su lugar, ser dirigido a un objetivo.
Asuma un pedido en la lista de invitados y use una regla
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪A ( X) ∧ A ( Y)A ( W)A ( W)XY→ A ( Z) ,→ A ( X) ,→ A ( Y) ,< Z,< Z⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⊢A ( W) → A ( Z)
(Usamos lugar de A t t e n d ( X ) para mantenerlo corto)A ( X)A t t e n d( X)
Esta regla es fácil de implementar.
Un enfoque bastante ingenuo
Para follows
facilitar la lectura, sea la relación dada como entrada, y brings
sea su reverso.
Entonces la entrada está dada por
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]).
Y brings
se puede definir de la siguiente manera:
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
Si definimos
partymaker(X):-Guests=[ad,bm,cp,dd,ec,fa],member(X,Guests),brings(X,Guests).
Obtenemos las siguientes soluciones únicas:
[ad,ec]
Esta no es la lista completa, ya que bajo el orden alfabético de la cláusula
follows(bm,[cp,dd]).
no está trabajando.
Una solución bastante complicada para el rompecabezas original.
Para resolver el problema por completo, debe dejar que el sistema intente probar la asistencia para invitados posteriores sin introducir bucles infinitos en el árbol de búsqueda. Hay múltiples formas de lograr este objetivo. Cada uno tiene sus ventajas y desventajas.
Una forma es redefinir de la brings/2
siguiente manera:
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).
El último argumento brings/4
es necesario para evitar un bucle infinito try_bring
.
Esto da las siguientes respuestas: Albus, Carlotta, Elfrida y Falco. Sin embargo, esta solución no es la más eficiente ya que se introduce el retroceso donde a veces se puede evitar.
Una solución general
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).
Ahora si, por ejemplo, el conjunto de datos # 2 se da como
follows(fa,[dd,ec]).
follows(cp,[ad,bm]).
guests([ad,bm,cp,dd,ec,fa]).
Obtenemos la respuesta L = [[ad, bm, dd, ec]]. Lo que significa que todos los invitados excepto Carlotte y Falco deben ser invitados.
Las respuestas que me dio esta solución coincidieron con las soluciones dadas en el artículo de Wicked Witch con la excepción del conjunto de datos # 6, donde se produjeron más soluciones. Esta parece ser la solución correcta.
Finalmente, debo mencionar la biblioteca CLP (FD) de Prolog que es particularmente adecuada para este tipo de problemas.
attend(BM) :- attend(AD).
es exactamente lo mismo queattend(X) :- attend(Y).