Các vấn đề về sự hài lòng có thể được giải quyết với Prolog không?


18

Là loại "tham dự bữa tiệc" có thể giải quyết được trong Prolog không? Ví dụ:

Burdock Muldoon và Carlotta Pinkstone đều nói rằng họ sẽ đến nếu Albus Dumbledore đến. Albus Dumbledore và Daisy Dodderidge đều nói rằng họ sẽ đến nếu Carlotta Pinkstone đến. Albus Dumbledore, Burdock Muldoon và Carlotta Pinkstone đều nói rằng họ sẽ đến nếu Elfrida Clagg đến. Carlotta Pinkstone và Daisy Dodderidge đều nói rằng họ sẽ đến nếu Falco Aesalon đến. Burdock Muldoon, Elfrida Clagg và Falco Aesalon đều nói rằng họ sẽ đến nếu cả Carlotta Pinkstone và Daisy Dodderidge đều đến. Daisy Dodderidge cho biết cô sẽ đến nếu cả Albus Dumbledore và Burdock Muldoon đều đến. Ai cần được thuyết phục tham dự bữa tiệc để đảm bảo rằng tất cả những người được mời của cô tham dự?

Tôi đã cố gắng diễn đạt điều này trong GNU Prolog:

attend(BM) :- attend(AD).
attend(CP) :- attend(AD).
attend(AD) :- attend(CP).
attend(DD) :- attend(CP). 
attend(AD) :- attend(EC).
attend(BM) :- attend(EC).
attend(CP) :- attend(EC). 
attend(CP) :- attend(FA).
attend(DD) :- attend(FA).
attend(BM) :- attend(CP),attend(DD).
attend(EC) :- attend(CP),attend(DD).
attend(FA) :- attend(CP),attend(DD).
attend(DD) :- attend(AD),attend(BM).

attend(FA). /* try different seed invitees in order to see if all would attend*/

/* input:
write('invited:'),nl,
  attend(X),write(X),nl,
  fail.*/

Tôi đang gặp tình trạng tràn stack (không chơi chữ) và không có kiến ​​thức về đánh giá prolog, đây là lý do tại sao tôi hỏi.

Nói chung, vấn đề này có thể được đưa vào công thức hài lòng Boolean CNF (với 6 biến boolean). Do đó, quan điểm prolog có công đức gì không?


2
Tôi nghĩ vấn đề của bạn là số nhận dạng chữ hoa là các biến, do đó, attend(BM) :- attend(AD).chính xác giống nhưattend(X) :- attend(Y).
svick

Đã thử với các chữ cái nhỏ ( ideone.com/w622Z ) vẫn cho kết quả tương tự.
Tegiri Nenashi

Tôi rõ ràng đã không thực hiện bất kỳ Sao Thủy / Prolog nào quá lâu, tất nhiên quan điểm của Svick là chính xác và chương trình đầu tiên của bạn gần như tương ứng với câu nói "một người nào đó được thừa nhận nếu một người nào đó được thừa nhận". Sau khi thay thế các biến bằng các thuật ngữ cụ thể, bạn sẽ gặp vấn đề được giải thích trong câu trả lời của tôi.
Bến

Câu trả lời đơn giản là "Có", vì Prolog là ngôn ngữ hoàn chỉnh Turing.
David Richerby

Câu trả lời:


13

Để giải quyết vấn đề với Prolog, như với bất kỳ ngôn ngữ lập trình nào, dù là khai báo hay bắt buộc, bạn phải suy nghĩ về cách trình bày giải pháp và đầu vào.

Vì đây là câu hỏi về lập trình, nên nó đã trở nên phổ biến trên StackOverflow.com nơi các lập trình viên giải quyết các vấn đề lập trình. Ở đây tôi sẽ cố gắng để khoa học hơn.

Attend(X)Attend(Y)Attend(Z)Attend(AD)Attend(BM)Attend(DD)

Daisy Dodderidge cho biết cô sẽ đến nếu cả Albus Dumbledore và Burdock Muldoon đều đến

khó điều trị hơn.

Với Prolog, cách tiếp cận đơn giản đầu tiên là tránh sự đảo ngược hoàn toàn mối quan hệ và thay vào đó là mục tiêu hướng đến.

Giả sử đặt hàng trong danh sách khách và sử dụng quy tắc

{A(X)A(Y)A(Z),A(W)A(X),A(W)A(Y),X<Z,Y<Z}A(W)A(Z)

A(X)Attend(X)

Quy tắc này rất dễ thực hiện.

Một cách tiếp cận khá ngây thơ

Đối với khả năng đọc, hãy followslà mối quan hệ được đưa ra làm đầu vào và bringsngược lại.

Sau đó, đầu vào được đưa ra bởi

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]).

bringscó thể được định nghĩa như sau:

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

Nếu chúng ta xác định

 partymaker(X):-Guests=[ad,bm,cp,dd,ec,fa],member(X,Guests),brings(X,Guests).

Chúng tôi nhận được các giải pháp độc đáo sau:

 [ad,ec]

Đây không phải là danh sách đầy đủ, vì theo thứ tự chữ cái mệnh đề

 follows(bm,[cp,dd]).

không hoạt động.

Một giải pháp khá liên quan đến câu đố gốc

Để giải quyết vấn đề hoàn toàn, bạn phải thực sự để hệ thống cố gắng chứng minh sự tham dự của những vị khách sau này mà không giới thiệu các vòng lặp vô hạn cho cây tìm kiếm. Có nhiều cách để thực hiện mục tiêu này. Mỗi cái đều có ưu điểm và nhược điểm.

Một cách là xác định lại brings/2như sau:

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).

Đối số cuối cùng brings/4là cần thiết để tránh một vòng lặp vô hạn trong try_bring.

Điều này đưa ra các câu trả lời sau: Albus, Carlotta, Elfrida và Falco. Tuy nhiên, giải pháp này không phải là giải pháp hiệu quả nhất vì việc quay lui được giới thiệu mà đôi khi có thể tránh được.

Một giải pháp chung

r(X,S):VV

SVV=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).

Bây giờ nếu ví dụ số liệu # 2 được đưa ra là

follows(fa,[dd,ec]).
follows(cp,[ad,bm]).
guests([ad,bm,cp,dd,ec,fa]).

Chúng tôi nhận được câu trả lời L = [[ad, bm, dd, ec]]. Điều đó có nghĩa là tất cả khách nhưng Carlotte và Falco phải được mời.

Các câu trả lời mà giải pháp này đưa ra cho tôi phù hợp với các giải pháp được đưa ra trong bài viết Phù thủy độc ác ngoại trừ tập dữ liệu số 6, nơi có nhiều giải pháp được sản xuất. Đây dường như là giải pháp chính xác.

Cuối cùng, tôi phải đề cập đến thư viện Prolog của CLP (FD) đặc biệt phù hợp với loại vấn đề này.


Câu trả lời đúng cũng bao gồm F (tức là A, C, E, F). Bạn có lỗi đánh máy trong các quy tắc hoặc vấn đề nghiêm trọng hơn trong chương trình.
Tegiri Nenashi


Bộ dữ liệu số 2 từ trang web được liên kết trong bài viết ideone.com/21AmX Nó dường như không hoạt động ...
Tegiri Nenashi

Giải pháp của bạn có xử lý nhiều lựa chọn thay thế (tập dữ liệu số 8) ideone.com/rBjXi
Tegiri Nenashi

@TegiriNenashi Có 6 giả định "không giả định" tại trang web được liên kết. Giải pháp của tôi không thỏa mãn № 2 và № 5. 5 có vẻ dễ sửa: khái quát hai quy tắc "% Không phải là người theo dõi". Nếu điều đó được khắc phục, nó sẽ nhận được câu trả lời đầu tiên cho tập dữ liệu # 8. Cho đến khi giả định № 2 được thỏa mãn, không có bộ dữ liệu mẫu nào có thể được giải chính xác.
Dmitri Chubarov

10

Như được phát hiện bởi svick, vấn đề đầu tiên với mã trong OP là tên bắt đầu bằng chữ in hoa là biến trong Prolog. Vì vậy, admit(CP) :- admit(AD)tương đương với attend(X) :- attend(Y), kết quả là Prolog ngay lập tức đi vào một vòng lặp vô hạn đang cố gắng chứng minh rằng attendgiữ cho một số thuật ngữ bằng cách tìm một số thuật ngữ cho attendgiữ.

Tuy nhiên, nếu bạn có nghĩa là mỗi nhóm tên viết tắt là một thuật ngữ riêng biệt cụ thể, thì bạn vẫn sẽ gặp phải tình trạng tràn ngăn xếp vì bạn có chu kỳ, ví dụ:

attend(cp) :- attend(ad).
attend(ad) :- attend(cp).

Vì vậy, để tìm hiểu xem liệu attend(cp)giữ Prolog sẽ cố gắng xác định xem attend(ad)có giữ hay không , bằng cách kiểm tra xem attend(cp)có giữ hay không , cho đến khi ngăn xếp tràn.

Tôi không tin vanilla Prolog thực hiện bất kỳ nỗ lực nào để xác định liệu có một chu trình như vậy hay không và kiểm tra các cách khác để tạo ra một attend(cp)hoặc attend(ad)đúng hơn là bị mắc kẹt trong một vòng lặp vô hạn.

Có thể có hoặc không có phiên bản Prolog hỗ trợ tính năng như vậy. Tôi quen thuộc hơn với Sao Thủy và tôi nghĩ rằng "mô hình tối thiểu" của Mercury chính xác là những gì bạn cần cho trường hợp này. Tôi chưa bao giờ thực sự sử dụng nó, nhưng sự hiểu biết của tôi ít nhiều cho phép một thuật ngữ ngụ ý được coi là đúng nếu có một cách nào khác để chứng minh nó, hoặc sai, nếu không bị vướng vào một vòng lặp vô hạn. Xem phần có liên quan của tài liệu Sao Thủy và nếu bạn quan tâm một bài viết mô tả việc thực hiện.

Mercury là một ngôn ngữ lập trình logic có độ tinh khiết được thi hành, với cú pháp tương tự như Prolog nhưng hệ thống chế độ và kiểu mạnh, và nó được biên dịch thay vì diễn giải.

Tôi vừa đọc lại phần giới thiệu về bài báo (mà tôi đã không đọc trong một thời gian) và nó đề cập đến việc lập bảng đã được triển khai trong một số phiên bản của Prologs, vì vậy có thể bạn có thể tiến xa hơn bằng cách googling để lập bảng trong Prolog.



0

Đặt vấn đề chữ thường / chữ hoa sang một bên, có một chu kỳ trong các mệnh đề:

attend(cp) :- attend(ad).
attend(ad) :- attend(cp).

Vì vậy, khi bạn gọi một trình thông dịch từ trên xuống, nó sẽ lặp. Bạn có thể gặp nhiều may mắn hơn với Lập trình Trả lời (ASP), hoạt động từ dưới lên. Đây là một mã hóa thông qua thư viện ( tối thiểu / asp ):

:- use_module(library(minimal/asp)).

choose([admit(bm)]) <= posted(admit(ad)).
choose([admit(cp)]) <= posted(admit(ad)).
choose([admit(ad)]) <= posted(admit(cp)).
choose([admit(dd)]) <= posted(admit(cp)).
choose([admit(ad)]) <= posted(admit(ec)).
choose([admit(bm)]) <= posted(admit(ec)).
choose([admit(cp)]) <= posted(admit(ec)).
choose([admit(cp)]) <= posted(admit(fa)).
choose([admit(dd)]) <= posted(admit(fa)).
choose([admit(bm)]) <= posted(admit(cp)),posted(admit(dd)).
choose([admit(ec)]) <= posted(admit(cp)),posted(admit(dd)).
choose([admit(fa)]) <= posted(admit(cp)),posted(admit(dd)).
choose([admit(dd)]) <= posted(admit(ad)),posted(admit(bm)).

choose([admit(fa)]) <= posted(init).

Dưới đây là một ví dụ chạy:

Jekejeke Prolog 3, Runtime Library 1.3.8 (23 May 2019)

?- post(init), listing(admit/1).
admit(fa).
admit(cp).
admit(ad).
admit(bm).
admit(dd).
admit(ec).
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.