Загадка Эйнштейна Пролог

Мне нужна помощь с домашним заданием по прологу для моего класса AI. Вопрос в том, чтобы написать код пролога к головоломке Эйнштейна. Я знаю, как написать это самостоятельно, но в домашней работе есть некоторые ограничения.

 there are 5 houses
 the Englishman lives in the red house
 the Spaniard owns the dog
 coffee is drunk in the green house
 the Ukrainian drinks tea
 the green house is immediately to the right of the ivory house
 the Old Gold smoker owns snails
 Kools are smoked in the yellow house
 milk is drunk in the middle house
 the Norwegian lives in the first house
 the man who smokes Chesterelds lives in the house next to the man with the fox
 3 Kools are smoked in the house next to the house where the horse is kept
 the Lucky Strike smoker drinks orange juice
 the Japanese smokes Parliaments
 the Norwegian lives next to the blue house

Я знаю, что мне нужно использовать список для домов, потому что они заказаны. Я хотел использовать список и для характеристик дома, но тут возникла проблема.

Я собирался использовать анонимные переменные house (englishman, red, _, _, _). но я не знаю, как интерпретировать это для домашнего задания.

Вот ограничения: вы должны использовать следующие двоичные символы предиката:

owns(N,Pet)
smokes(N, Cigarette).
drinks(N, Drink).

В остальном вы можете использовать любое количество предикатов.

вот как я инициализировал факты, но я не знаю, как создавать правила в этом случае

next_to(X,Y) :- right_of(X,Y); right_of(Y,X).

owns(spaniard, dog).
drinks(ukrainian, tea).
smokes(japanese, parliaments).
right_of(ivory, green).
lives(englishman, red).
owns(X, snail) :- smokes(X, old_gold).
smokes(X, kools) :- owns(X, yellow).
smokes(X, lucky_strike) :- drinks(X, orange_juice).
drinks(X, coffee) :- owns(X, green_house).

это немного имеет смысл, но в то же время выглядит совершенно неправильным. Я не думаю, что смогу куда-нибудь пойти с этим. : /


person Gasim    schedule 12.02.2012    source источник
comment
tbh, я думаю, что эти ограничения обеспечивают не очень подходящее представление данных и, следовательно, приводят к неудобному / не очень элегантному коду. Я использовал аналогичные предикаты при первом дубле, но после небольшого удаления файла (окончательное решение: [users.ntua.gr/el06009/files/zebra.pl]). извините за не совсем полезный комментарий, просто хотел сказать, что эти ограничения действительно неприятны: /   -  person Thanos Tintinidis    schedule 13.02.2012
comment
@thanosQR Решение Prolog находится здесь. Я считаю, что это элегантно и красиво. :) ключ использует select для одновременного взаимоисключающего выбора из домена, чтобы ограничение diff удовлетворялось путем построения.   -  person Will Ness    schedule 18.11.2013
comment
@WillNess ну, вы же не используете owns/2, smokes/2 и drinks/2, верно? Я не говорю, что у вас не может быть элегантного решения в прологе (наоборот, я раньше думал, что мое решение довольно элегантно, пока не увидел ваше (ссылка выше выглядит неработающей github.com/thanosqr/side_projects/blob/master/zebra_puzzle.pl)), просто домашнее задание ограничения затрудняют написание элегантного решения   -  person Thanos Tintinidis    schedule 19.11.2013
comment
@thanosQR очень интересный подход! :) 1+1 = 1+1 действительно. Хороший!   -  person Will Ness    schedule 19.11.2013
comment
@thanosQR ну, я добавил сюда ответ, который использует owns/2 и тому подобное, и это тоже не слишком уродливо. :) Делает в 75 раз больше выводов, чем мой другой ответ, но все равно работает очень быстро.   -  person Will Ness    schedule 20.11.2013


Ответы (3)


Этот сайт посвящен решению таких головоломок с помощью CLP (FD). Но полная мощь CLP (FD) здесь излишня: ваше задание может быть решено с эффективным поиском во всем пространстве решений, если вы адекватно описали ограничения.

Решение будет состоять из 5 домов, каждый атрибут которых удовлетворяет всем ограничениям, налагаемым описанием.

Помните, что для каждого атрибута следует использовать один и тот же символ (т.е. зеленый и green_house неверно, выберите один из них).

Также next_to кажется неправильным: если вы нумеруете от 1 до 5, это может быть вычислено или перечислено, но относится к ближайшему соседу.

Итак, завершите представление данных "пространство поиска решения", что-то вроде

Problem = [
 house(1, Nationality1, Color1, Pet1, Drinks1, Smokes1),
 house(2, Nationality2, Color2, Pet2, Drinks2, Smokes2),
 ...
],
% place constraints
member(house(_, englishman, red, _, _, _), Problem),
member(house(_, spaniard, _, dog, _, _), Problem),
...

member / 2 это более простая встроенная функция Prolog, но в этом случае ее достаточно для решения проблемы: когда все ограничения опубликованы, переменные будут привязаны к соответствующим значениям. Ключевым моментом является способность члена недетерминированно выбирать член (да) решения.

Поэтому, когда вам нужно выразить ограничение между двумя разными элементами, вызовите член дважды и поместите ограничения между соответствующими переменными: т.е.

человек, который курит Chesterelds, живет в доме рядом с человеком с лисой

будет переведен на

....,
member(house(N, _, _, _, _, chesterelds), Problem),
member(house(M, _, _, fox, _, _), Problem),
next_to(N, M),
...

Выражая таким образом множество ограничений, остерегайтесь идентичности символов: может быть полезно закодировать каждый предикат в отдельной процедуре, чтобы избежать излишнего наложения имен. Но верна и обратная сторона: если один и тот же символ используется не только в ограничении, необходимо будет обойти символ, чтобы сузить область поиска.

Я позволю вам подумать о правильном представлении «геометрических» предикатов: next_to и right_of можно перечислить или выразить с помощью арифметики.

person CapelliC    schedule 13.02.2012

Эта головоломка (также известная как головоломка с зеброй) много раз обсуждалась ранее в Stackoverflow, см., Например:

person Kaarel    schedule 15.02.2012

Перевод Пролога может быть простым, правило за правилом, по-прежнему следуя парадигме создания экземпляра домена путем выбора из него . Здесь это область атрибутов дома; в связанном ответе атрибуты дома фиксируются программистом, а домен представляет собой фактические жилые дома, что позволяет очень сжатое кодирование.

Другими словами, разница заключается в нотации: сложная нотация уже уводит нас на полпути, но это был человек, который изобрел ее и последовал ей (как программист, имеющий записать norwegian в первую спецификацию дома напрямую, в соответствующей позиции аргумента position) - не компьютер.

Здесь мы стараемся внести в код как можно меньше человеческих знаний, следуя ограничениям домашнего задания. (хотя, конечно, все можно спорить, и в конечном итоге в предотвращении вмешательства человека будет компьютерная программа, которая принимает английский текст в качестве входных данных ... что снова будет открыто для критики относительно того, насколько конкретно эта программа предназначена для поиска решения этой конкретной головоломки или типа головоломок и т. д. и т. д.)

Мы кодируем его в стиле сверху вниз. Видимо вопрос отсутствует. Это должно быть «кто пьет воду? Кому принадлежит зебра?»:

zebra( Z, W ,HS) :-         
    length(        HS, 5),      % nation? color? what's that? define it later...
    member(  H1,   HS),    nation( H1, eng    ),    color( H1, red    ),
    member(  H2,   HS),    nation( H2, spa    ),    owns(  H2, dog    ),            
    member(  H3,   HS),    drink(  H3, coffee ),    color( H3, green  ),         
    member(  H4,   HS),    nation( H4, ukr    ),    drink( H4, tea    ),
    right_of(B, A, HS),    color(  A , ivory  ),    color( B , green  ),
    member(  H5,   HS),    smoke(  H5, oldgold),    owns(  H5, snails ),   
    member(  H6,   HS),    smoke(  H6, kools  ),    color( H6, yellow ), 
    middle(  C,    HS),    drink(  C , milk   ),  
    first(   D,    HS),    nation( D , nor    ),
    next_to( E, F, HS),    smoke(  E , chester),    owns(  F , fox    ),
    next_to( G, H, HS),    smoke(  G , kools  ),    owns(  H , horse  ),
    member(  H7,   HS),    smoke(  H7, lucky  ),    drink( H7, orange ),
    member(  H8,   HS),    nation( H8, jpn    ),    smoke( H8, parlamt),
    next_to( I, J, HS),    nation( I , nor    ),    color( J , blue   ),
    member(  W,    HS),    drink(  W , water  ),
    member(  Z,    HS),    owns(   Z , zebra  ).

right_of( B, A, HS) :- append( _, [A, B | _], HS).
next_to( A, B, HS) :- right_of( B, A, HS) ; right_of( A, B, HS).
middle( A, [_,_,A,_,_]).
first( A, [A | _]).

nation(H, V) :-  attr( H, nation-V).
owns(  H, V) :-  attr( H, owns-V).        % select an attribute
smoke( H, V) :-  attr( H, smoke-V).       %   from an extensible record H
color( H, V) :-  attr( H, color-V).       %   of house attributes
drink( H, V) :-  attr( H, drink-V).       %   which *is* a house

attr(House, Attr-Value) :- 
    memberchk( Attr-X, House),            % unique attribute names
    X = Value.

Тестирование, выполнение исчерпывающего поиска с отказоустойчивым циклом,

3 ?- time((zebra(Z,W,_), maplist(nation,[Z,W],R), writeln(R), false ; true)).
[jpn,nor]
% 180,974 inferences, 0.016 CPU in 0.020 seconds (78% CPU, 11600823 Lips)
true.

Вот как определяются дома:

5 ?- zebra(_, _, HS), maplist( writeln, HS),
     false.
[smoke-kools,  color-yellow, nation-nor,    owns-fox,      drink-water |_G859]
[nation-ukr,   drink-tea,    smoke-chester, owns-horse,    color-blue  |_G853]
[nation-eng,   color-red,    smoke-oldgold, owns-snails,   drink-milk  |_G775]
[nation-spa,   owns-dog,     color-ivory,   smoke-lucky,   drink-orange|_G826]
[drink-coffee, color-green,  nation-jpn,    smoke-parlamt, owns-zebra  |_G865]
false.

или, если списки атрибутов "заморожены" путем фиксации их длины, а затем отсортированы,

7 ?- zebra( _, _, HS), maplist( length, HS, _), !, maplist( sort, HS, S),
     maplist( writeln, S), false.
[color-yellow, drink-water,  nation-nor,  owns-fox,    smoke-kools  ]
[color-blue,   drink-tea,    nation-ukr,  owns-horse,  smoke-chester]
[color-red,    drink-milk,   nation-eng,  owns-snails, smoke-oldgold]
[color-ivory,  drink-orange, nation-spa,  owns-dog,    smoke-lucky  ]
[color-green,  drink-coffee, nation-jpn,  owns-zebra,  smoke-parlamt]
false.

Также легко сделать так, чтобы предикат attr/2 принимал списки Name-Value пар, что позволяет более естественно плавный, высокоуровневый стиль кодирования с своего рода «расширяемыми записями» - можно даже сказать «объектами» - спецификациями, например

zebra( Z, W ,HS):-         
    length(       HS, 5), 
    member(  H1,  HS),    attr( H1,  [nation-eng,   color-red  ] ),
    member(  H2,  HS),    attr( H2,  [nation-spa,   owns-dog   ] ),
    member(  H3,  HS),    attr( H3,  [drink-coffee, color-green] ),
    ......

и т. д..

person Will Ness    schedule 20.11.2013
comment
Хотя бы отметку, пожалуйста. Разве нет общего названия для такого рода загадок, например: можно решить, нарисовав пару таблиц. Именно так думал об этом Эйнштейн - и он утверждал, что только крошечный процент людей способен решить эту проблему. Увы, этот крошечный процент здесь, на SO! - person false; 20.11.2013
comment
здесь все немного по-другому: они не хотят, чтобы мы рисовали стол, а изобрели способ сказать компьютеру «владеет», «курит» и т. д. Вот почему я решил добавить этот ответ. Думаю, это достаточно другой подход. - person Will Ness; 20.11.2013
comment
@false ну технически вы не должны решать это с помощью программы XD - person Thanos Tintinidis; 09.12.2013
comment
@thanosQR - это своего рода расширяемые записи, которые очень легко сделать на Prolog. :) Я даже позже немного обобщил это с помощью attr( Record, FieldValuePairs). Я особенно понравилось замораживать записи здесь с помощью maplist(length,Records, _). :) - person Will Ness; 09.12.2013
comment
@WillNess Естественно, следующим шагом будет написание программы, которая принимает конфигурацию данных, а затем решает ее: D - person Thanos Tintinidis; 09.12.2013