ปัญหาความพึงพอใจของข้อ จำกัด สามารถแก้ไขได้ด้วย Prolog หรือไม่


18

คือ"การเข้าร่วมประชุมพรรค"ประเภทของปัญหาแก้ไขได้ในเปิดฉาก? ตัวอย่างเช่น:

หญ้าเจ้าชู้ Muldoon และ Carlotta Pinkstone ทั้งคู่บอกว่าพวกเขาจะมาถ้า Albus Dumbledore มา Albus Dumbledore และ Daisy Dodderidge ทั้งคู่บอกว่าพวกเขาจะมาถ้า Carlotta Pinkstone มา Albus Dumbledore, Burdock Muldoon และ Carlotta Pinkstone ทุกคนบอกว่าพวกเขาจะมาถ้า Elfrida Clagg มา Carlotta Pinkstone และ Daisy Dodderidge ทั้งคู่กล่าวว่าพวกเขาจะมาหาก Falco Aesalon มา Burdock Muldoon, Elfrida Clagg และ Falco Aesalon ทุกคนบอกว่าพวกเขาจะมาถ้า Carlotta Pinkstone และ Daisy Dodderidge ทั้งคู่เข้ามา เดซี่เดอเดอริดจ์บอกว่าเธอจะมาถ้าอัลบัสดัมเบิลดอร์และหญ้าเจ้าชู้ Muldoon มาทั้งคู่ ใครบ้างที่จะต้องถูกชักชวนให้เข้าร่วมงานเลี้ยงเพื่อให้แน่ใจว่าผู้ได้รับเชิญของเธอทั้งหมดเข้าร่วม?

ฉันได้พยายามที่จะแสดงสิ่งนี้ใน 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.*/

ฉันกำลังประสบปัญหาสแต็คล้น (ไม่มีการเล่นสำนวน) และไม่มีความรู้ในการประเมินผลการเปิดฉากนี่คือเหตุผลที่ฉันถาม

โดยทั่วไปแล้วปัญหานี้สามารถนำไปใช้กับสูตรความพึงพอใจ Boolean CNF (ด้วยตัวแปรบูลีน 6 ตัว) ดังนั้นมุมมองของอารัมภบทจึงมีข้อดีหรือไม่?


2
ฉันคิดว่าปัญหาของคุณคือตัวบ่งชี้ตัวพิมพ์ใหญ่เป็นตัวแปรดังนั้นจึงattend(BM) :- attend(AD).เหมือนกับattend(X) :- attend(Y).
svick

พยายามด้วยตัวอักษรขนาดเล็ก ( ideone.com/w622Z ) ยังคงเป็นผลลัพธ์เดียวกัน
Tegiri Nenashi

เห็นได้ชัดว่าฉันไม่ได้ทำ Mercury / Prolog นานเกินไปแน่นอนว่าจุด svick นั้นถูกต้องและโปรแกรมแรกของคุณสอดคล้องกับการพูดว่า "บางคนยอมรับถ้ามีคนยอมรับ" ต้องแทนที่ตัวแปรด้วยคำที่เป็นรูปธรรมจากนั้นคุณพบปัญหาที่อธิบายในคำตอบของฉัน
เบ็น

คำตอบง่ายๆคือ "ใช่" เนื่องจาก Prolog เป็นภาษาทัวริงที่สมบูรณ์
David Richerby

คำตอบ:


13

ในการแก้ปัญหาด้วย Prolog เช่นเดียวกับภาษาการเขียนโปรแกรมใด ๆ ไม่ว่าจะเป็นการประกาศหรือจำเป็นคุณต้องคิดถึงการเป็นตัวแทนของการแก้ปัญหาและการป้อนข้อมูล

เนื่องจากนี่เป็นคำถามการเขียนโปรแกรมจึงเป็นที่นิยมใน StackOverflow.com ที่โปรแกรมเมอร์แก้ปัญหาการเขียนโปรแกรม ที่นี่ฉันจะพยายามเป็นวิทยาศาสตร์มากขึ้น

ในการแก้ปัญหาใน OP หนึ่งจะต้องย้อนกลับความสัมพันธ์ที่กำหนดโดยการอ้างอิงที่ระบุไว้ในอินพุต ข้อของแบบฟอร์มทีทีอีn d ( X ) ทีทีอีn d ( Y ) ทีทีอีn d ( Z )เป็นเรื่องง่ายที่จะย้อนกลับ ข้อทีทีอีn d ( D ) ทีทีอีn d (Aเสื้อเสื้ออีnd(X)Aเสื้อเสื้ออีnd(Y)Aเสื้อเสื้ออีnd(Z)เช่นAเสื้อเสื้ออีnd(AD)Aเสื้อเสื้ออีnd(BM)Aเสื้อเสื้ออีnd(DD)

เดซี่โดเดริดจ์บอกว่าเธอจะมาถ้าอัลบัสดัมเบิลดอร์และหญ้าเจ้าชู้ Muldoon มาทั้งคู่

ยากต่อการรักษา

ด้วย Prolog วิธีการง่าย ๆ แรกคือการหลีกเลี่ยงการย้อนกลับของความสัมพันธ์และเป้าหมายโดยตรงแทน

สมมติว่ามีการสั่งซื้อในรายชื่อแขกและใช้กฎ

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

(เราใช้( X )แทนทีทีอีn d ( X )ที่จะให้มันสั้น)A(X)Aเสื้อเสื้ออีnd(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)X

ถ้าเรากำหนด

 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

นี่จะให้คำตอบต่อไปนี้: Albus, Carlotta, Elfrida และ Falco อย่างไรก็ตามวิธีนี้ไม่ใช่วิธีที่มีประสิทธิภาพมากที่สุดเนื่องจากมีการแนะนำการย้อนรอยซึ่งบางครั้งสามารถหลีกเลี่ยงได้

วิธีแก้ปัญหาทั่วไป

R(X,S):VV'

SVV'=V{X}

VยูV

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]] ซึ่งหมายความว่าแขกทุกคน แต่ Carlotte และ Falco จะต้องได้รับเชิญ

คำตอบของวิธีแก้ปัญหานี้ให้ฉันตรงกับคำตอบในบทความ Wicked Witch ยกเว้นชุดข้อมูล # 6 ซึ่งมีวิธีแก้ไขเพิ่มเติม นี่น่าจะเป็นทางออกที่ถูกต้อง

ในที่สุดฉันต้องพูดถึงห้องสมุด CLP (FD) ของ Prolog ที่เหมาะสมเป็นพิเศษสำหรับปัญหาประเภทนี้


คำตอบที่ถูกต้องรวมถึง F (เช่น A, C, E, F) คุณพิมพ์ผิดในกฎหรือมีปัญหาร้ายแรงมากขึ้นในโปรแกรม
Tegiri Nenashi

ยืนยันแล้ว: ideone.com/Q3pqU
Tegiri Nenashi

ชุดข้อมูล # 2 จากเว็บไซต์ที่เชื่อมโยงในบทความideone.com/21AmXดูเหมือนจะไม่ทำงาน ...
Tegiri Nenashi

โซลูชันของคุณจัดการกับทางเลือกหลายตัว (ชุดข้อมูล # 8) ideone.com/rBjXi หรือไม่
Tegiri Nenashi

@TegiriNenashi มีข้อสันนิษฐาน 6 ข้อที่ไม่ได้เชื่อมโยงกับเว็บไซต์ โซลูชันของฉันไม่เป็นไปตาม№ 2 และ№ 5. № 5 ดูเหมือนง่ายต่อการแก้ไข: สรุปกฎ "% ไม่ใช่ผู้ติดตาม" สองข้อ หากได้รับการแก้ไขควรได้รับคำตอบแรกสำหรับชุดข้อมูล # 8 จนกว่าข้อสันนิษฐาน№ 2 จะไม่เป็นที่พอใจทั้งชุดข้อมูลตัวอย่างสามารถแก้ไขได้อย่างถูกต้อง
Dmitri Chubarov

10

ตามที่เห็นโดย svick ปัญหาแรกที่มีรหัสใน OP คือชื่อที่ขึ้นต้นด้วยตัวอักษรตัวพิมพ์ใหญ่เป็นตัวแปรใน Prolog ดังนั้นadmit(CP) :- admit(AD)เทียบเท่ากับattend(X) :- attend(Y)ผลลัพธ์ใน Prolog ทันทีที่เข้าสู่วงวนอนันต์ที่พยายามแสดงให้เห็นว่าattendเก็บคำไว้บางคำด้วยการค้นหาคำบางคำที่attendถือ

อย่างไรก็ตามหากคุณหมายถึงชื่อย่อแต่ละชุดว่าเป็นคำที่เป็นรูปธรรมชัดเจนคุณจะยังคงพบกับการโอเวอร์โฟลว์ของสแต็กเพราะคุณมีวัฏจักรเช่น

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

ดังนั้นในattend(cp)การตรวจสอบว่า Prolog จะพยายามตรวจสอบว่าattend(ad)มีการพักหรือไม่ซึ่งจะทำโดยการตรวจสอบว่าattend(cp)มีการพักหรือไม่และจนกว่าจะล้นสแต็ค

ฉันไม่เชื่อว่า vanilla Prolog ใช้ความพยายามในการพิจารณาว่ามีวัฏจักรดังกล่าวอยู่หรือไม่และตรวจสอบวิธีอื่น ๆในการสร้างหนึ่งattend(cp)หรือattend(ad)เป็นจริงมากกว่าติดอยู่ในวงวนไม่สิ้นสุด

อาจมีหรือไม่เป็นรุ่น Prolog ที่สนับสนุนคุณสมบัติดังกล่าว ฉันคุ้นเคยกับ Mercury มากขึ้นและฉันคิดว่า "การจัดวางแบบจำลองขั้นต่ำ" ของ Mercury เป็นสิ่งที่คุณต้องการสำหรับกรณีนี้ ฉันไม่เคยใช้มันมาก่อน แต่ความเข้าใจของฉันคือมันมากขึ้นหรือน้อยลงช่วยให้คำที่มีความหมายว่าตัวเองได้รับการพิจารณาว่าเป็นจริงหากมีวิธีอื่นในการพิสูจน์หรือเท็จอย่างอื่นโดยไม่ถูกวนซ้ำไม่รู้จบ ดูส่วนที่เกี่ยวข้องของเอกสารปรอทและหากคุณสนใจเอกสารอธิบายการใช้งาน

เมอร์คิวรี่เป็นภาษาการเขียนโปรแกรมแบบลอจิคัลที่มีความบริสุทธิ์และมีรูปแบบคล้ายกับภาษาโปรล็อก แต่เป็นระบบที่มีรูปแบบและโหมดที่แข็งแกร่ง

ฉันเพิ่งอ่าน skimmed ครั้งแรกไปยังบทความ (ซึ่งฉันไม่ได้อ่านในขณะนี้) และมันกล่าวถึง tabling ที่มีการใช้งานใน Prologs หลายรุ่นดังนั้นอาจเป็นไปได้ว่าคุณสามารถไปต่อได้โดย googling สำหรับ tabling ในอารัมภบท


4

ฉันพบบทความต่อไปนี้เกี่ยวกับการแก้ SAT โดยใช้คำนำ:

การดำเนินการแก้ปัญหาที่สามารถพบได้ที่นี่

ดูคำตอบ stackoverflow นี้สำหรับรายละเอียดเกี่ยวกับรหัสและวิธีใช้งาน


0

การวางปัญหาตัวพิมพ์เล็ก / ตัวพิมพ์ใหญ่ลงไปจะมีวงจรในส่วนคำสั่ง:

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

ดังนั้นเมื่อคุณเรียกล่ามจากบนลงล่างมันจะวนซ้ำ คุณอาจโชคดีกว่าด้วยAnswer Set Programming (ASP) ซึ่งใช้งานได้ดี นี่คือการเข้ารหัสผ่านไลบรารี ( ขั้นต่ำ / 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).

นี่คือตัวอย่างการเรียกใช้:

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).
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.