Разрешены ли проблемы типа «посещение вечеринки» в Прологе? Например:
Лопух Малдун и Карлотта Пинкстоун сказали, что придут, если придет Альбус Дамблдор. Альбус Дамблдор и Дейзи Доддридж оба сказали, что придут, если придет Карлотта Пинкстоун. Альбус Дамблдор, Бердок Малдун и Карлотта Пинкстоун сказали, что придут, если придет Эльфрида Клэгг. Карлотта Пинкстоун и Дейзи Доддридж сказали, что придут, если придет Фалько Эзалон. Лопух Малдун, Эльфрида Клэгг и Фалько Эзалон сказали, что придут, если придут Карлотта Пинкстоун и Дейзи Доддридж. Дейзи Доддридж сказала, что придет, если придут Альбус Дамблдор и Бердок Малдун. Кого нужно убедить присутствовать на вечеринке, чтобы обеспечить присутствие всех ее приглашенных?
Я попытался выразить это в 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.*/
Я испытываю переполнение стека (без каламбура) и не знаю, как вычислить пролог, поэтому и спрашиваю.
Вообще говоря, эту проблему можно привести к булевой формуле удовлетворения CNF (с 6 булевыми переменными). Следовательно, есть ли у перспективы пролога какие-либо достоинства?
источник
attend(BM) :- attend(AD).
точно так же, какattend(X) :- attend(Y).
Ответы:
Чтобы решить проблему с Прологом, как и с любым языком программирования, будь то декларативный или императивный, вам нужно подумать о представлении решения и входных данных.
Поскольку это вопрос программирования, он был бы популярен на StackOverflow.com, где программисты решают задачи программирования. Здесь я бы попытался быть более научным.
Чтобы решить проблему в OP, необходимо изменить отношение, определенное зависимостями, указанными во входных данных. Пункты вида т т е л д ( Х ) → т т е н д ( У ) ∧ т т е н д ( Z ) легко обратное. Пункты A t t e n d ( A D ) ∧ A t t e n d (Т т е н д( X) → т т е н д( Y) ∧ т т е н д( Z) какТ т е н д( Д ) ∧ т т е н д( Б М) → т т е н д( Д Д )
сложнее лечить.
С Прологом первый простой подход состоит в том, чтобы избежать полного изменения отношений и быть направленным на достижение цели.
Принять порядок в списке гостей и использовать правило
(Мы используем вместо A t t e n d ( X ), чтобы сократить его)А ( Х) Т т е н д( X)
Это правило легко реализовать.
Довольно наивный подход
Для удобства чтения позвольте
follows
быть отношением, данным как вход, иbrings
быть его обратным.Тогда вход дается
И
brings
может быть определено следующим образом:brings/3(X,L,S)
Если мы определим
Мы получаем следующие уникальные решения:
Это не полный список, так как под алфавитным порядком пункт
не работает.
Довольно сложное решение оригинальной головоломки
Чтобы полностью решить проблему, вы должны позволить системе попытаться доказать посещаемость для последующих гостей, не вводя бесконечные циклы в дерево поиска. Есть несколько способов достижения этой цели. У каждого есть свои преимущества и недостатки.
Одним из способов является переопределение
brings/2
следующим образом:Последний аргумент в
brings/4
необходим, чтобы избежать бесконечного цикла вtry_bring
.Это дает следующие ответы: Альбус, Карлотта, Эльфрида и Фалько. Однако это решение не является самым эффективным, поскольку вводится обратный путь, где его иногда можно избежать.
Общее решение
Теперь, если, например, набор данных № 2 задан как
Мы получаем ответ L = [[ad, bm, dd, ec]]. Это означает, что все гости, кроме Карлотты и Фалько, должны быть приглашены.
Ответы, которые дало мне это решение, совпали с решениями, приведенными в статье Wicked Witch, за исключением набора данных № 6, где было создано больше решений. Кажется, это правильное решение.
Наконец, я должен упомянуть CLP (FD) библиотеку Prolog, которая особенно подходит для такого рода проблем.
источник
Как заметил svick, первая проблема с кодом в OP заключается в том, что имена, начинающиеся с заглавных букв, являются переменными в Прологе. Так
admit(CP) :- admit(AD)
что эквивалентноattend(X) :- attend(Y)
, что приводит к Прологу сразу входя в бесконечный цикл , пытаясь доказать , чтоattend
имеет место в течение некоторого срока, находя некоторый срок , на которыйattend
имеет место.Однако, если вы хотите, чтобы каждый набор инициалов был конкретным отдельным термином, вы все равно столкнетесь с переполнением стека, потому что у вас есть циклы, например
Таким образом, чтобы выяснить,
attend(cp)
держит ли Пролог, попытается определить,attend(ad)
удерживает ли он , что он будет делать, проверяя,attend(cp)
держит ли , и так далее, до тех пор, пока стек не переполнится.Я не верю , что ваниль Пролог делает любую попытку определить , есть ли такой цикл, и рассмотреть другие способы , чтобы сделать один из
attend(cp)
илиattend(ad)
истинных , а не застрять в бесконечном цикле.Могут быть или не быть версии Prolog, которые поддерживают такую функцию. Я больше знаком с Меркурием и думаю, что «минимальная модель таблицы» Меркурия именно то, что вам нужно для этого случая. Я никогда не использовал его на самом деле, но, насколько я понимаю, он более или менее позволяет термину, который подразумевает, что он считается истинным, если есть какой-то другой способ доказать это, или ложным иным образом, не попадая в бесконечный цикл. См. Соответствующий раздел документации по Mercury, и, если вам интересно, документ с описанием реализации.
Mercury - это язык логического программирования с принудительной чистотой, с синтаксисом, аналогичным Prolog, но с сильными системами типов и режимов, и он скорее скомпилирован, чем интерпретирован.
Я только что пересмотрел введение в статью (которую я давно не читал), и в ней упоминается табулирование, реализованное в нескольких версиях Пролога, так что, возможно, вы сможете продвинуться дальше, прибегая к помощи табулирования в прологе.
источник
Я нашел следующую статью о решении SAT с использованием пролога:
Реализацию решателя можно найти здесь .
Посмотрите этот ответ stackoverflow для деталей о коде и как его использовать.
источник
Если оставить в стороне вопрос о нижнем / верхнем регистре, в предложениях есть цикл:
Поэтому, когда вы вызываете интерпретатора сверху вниз, он зацикливается. Возможно, вам больше повезет с программированием набора ответов (ASP), которое работает снизу вверх. Вот кодирование через библиотеку ( minimal / asp ):
Вот пример запуска:
источник