Щоб вирішити проблему з Prolog, як і з будь-якою мовою програмування, будь то декларативною чи імперативною, потрібно подумати про подання рішення та вхідні дані.
Оскільки це питання програмування, воно було б популярним на StackOverflow.com, де програмісти вирішують проблеми програмування. Тут я б намагався бути більш науковим.
Щоб вирішити задачу в ОП, потрібно змінити відношення, визначене залежностями, зазначеними у вхідних даних. Статті форми легко відміняти. Статті A t t e n d ( A D ) ∧ A t t e n d (A t t e n d( X) → A t t e n d( Y) ∧ A t t e n d( Z) якA t t e n d( A D ) ∧ A t t e n d( Б М) → A t t e n d( D D )
Дейзі Доддрідж сказала, що приїде, якщо приїдуть Альбус Дамблдор і Бердок Малдун
важче піддаються лікуванню.
З Прологом перший простий підхід полягає в тому, щоб уникнути повного розвороту відносин і бути націленою натомість.
Припустимо замовлення в списку гостей і скористайтеся правилом
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪A ( X) ∧ A ( Y)A ( Ш)A ( Ш)ХY→ A ( Z) ,→ A ( X) ,→ A ( Y) ,<Z,< Z⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⊢A ( Ш) → 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)
- це список гостей, до яких вже було доведено відвідування, якщо Х Відвідує.
Якщо ми визначимось
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).
Останній аргумент в brings/4
необхідний, щоб уникнути нескінченного циклу try_bring
.
Це дає наступні відповіді: Альбус, Карлотта, Ельфріда та Фалько. Однак це рішення не є найбільш ефективним, оскільки зворотний трек вводиться там, де його іноді можна уникнути.
Загальне рішення
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]]. Що означає, що треба запросити всіх гостей, окрім Карлотти та Фалько.
Відповіді на це рішення дали мені відповідність із рішеннями, наведеними у статті Wicked Witch, за винятком набору даних № 6, де було вироблено більше рішень. Це здається правильним рішенням.
Нарешті, я повинен згадати бібліотеку CLP (FD) Prolog, яка особливо підходить для подібних проблем.
attend(BM) :- attend(AD).
точно так само, якattend(X) :- attend(Y).