Кузнецов, Токарев, Слесарев и Резчиков играют в домино

Программирование Помощь с решением задач на Prolog Решение головоломок на Prolog Кузнецов, Токарев, Слесарев и Резчиков играют в домино

В этой теме 3 ответа, 2 участника, последнее обновление  Васильев Владимир Сергеевич 3 года/лет, 5 мес. назад.

Просмотр 4 сообщений - с 1 по 4 (из 4 всего)
  • Автор
    Сообщения
  • #1761

    questioner
    Участник

    Помогите решить логическую задачу:

    Четыре человека играют в домино.
    Их фамилии Кузнецов, Токарев, Слесарев и Резчиков.
    Профессия каждого игрока соответствует фамилии одного из других игроков.
    Напротив Кузнецова сидит слесарь.
    Напротив Резчикова сидит резчик.
    Справа от Слесарева сидит токарь.
    Кто сидит слева от кузнеца?

    Мой вариант решения:

    naprotiv(A,B,[_,A,_,B]).
    naprotiv(B,A,[A,_,B,_]).
    naprotiv(B,A,[_,A,_,B]).
      
    sprava(A,B,[A,B,_,_]).
    sprava(A,B,[_,_,A,B]).
      
    sleva(A,B,L):-
      sprava(B,A,L).
      
    insert(Y,XZ,XYZ):-
      append(X,Z,XZ),append(X,[Y],XY),append(XY,Z,XYZ).
      
    permute([],[]).
    permute([H|Tail],P):-
      permute(Tail,T),insert(H,T,P).
      
    find(Ans):-
      permute([kuznecov,tokarev,slesarev,rezchikov],[A,B,C,D]),
      permute([kuznec,tokar,slesar,rezchik],[Q1,Q2,Q3,Q4]),
      L=[m(A,Q1),m(B,Q2),m(C,Q3),m(D,Q4)],
      member(m(S1,kuznec),L),not(S1=kuznecov),
      member(m(S2,tokar),L),not(S2=tokarev),
      member(m(S3,slesar),L),not(S3=slesarev),
      member(m(S4,rezchik),L),
      Surnames=[A,B,C,D],
      naprotiv(kuznecov,S3,Surnames),
      sprava(slesarev,S2,Surnames),
      naprotiv(rezchikov,S4,Surnames),
      sleva(Ans,S1,Surnames).

    Мне не понятно что прога делает (я пытаюсь отладить ее по шагам в графическом виде, но мне это ничего не дает). Можете помочь с комментариями если вам не трудно? В прологе новичок,а задача нужна очень сильно.
    Статью про решение логических задач прочитал — не помогло.

    #1762

    С комментариями помогу если будут конкретные вопросы.
    Если коротко, то функция find:

    • перебирает все варианты фамилий и профессий в разных порядках;
    • помещает полученный вариант (набор профессий и фамилий) в список (так удобней обрабатывать);
    • проверяет чтобы профессии кузнеца, токаря и слесаря не совпадали с соответствующей фамилией;
    • строка следит за тем, чтобы напротив S3 (а S3 — это слесарь) сидит Кузнецов;
    #1763

    questioner
    Участник

    Я лишь мельком прочитал вашу статью и осознал, что мой случай не тривиален (дальше не читал).
    Конкретные вопросы:

    • сперва вызывается
      permute([kuznecov,tokarev,slesarev,rezchikov],[A,B,C,D])
      Это предикат, который имеет входной список (состоящий из фамилий) и выходной(A,B,C,D).
      Затем он рекурсивно помещается сюда permute([],[]), а затем попадает в
      permute([H|Tail],P):-permute(Tail,T),insert(H,T,P).

      где permute([H|Tail],P) разделяет список на голову и хвост, в P будет помещен результат.
      permute(Tail,T) обходит рекурсивно по всему списку (до тех пор, пока в списке не останется 1 значение — в данном случае Резчиков).
      Затем, передаем нашего Резчикова в предикат insert(H,T,P) — где H хранит Резчикова, T и P пустые параметры.

    • совсем не понятно что происходит дальше:

      append([],B,B).
      append([H|Tail],B,[H|NewTail]):-
        append(B,Tail,NewTail).
       
      member(H,[H|_]).
      member(H,[_|Tail]):-
        member(H,Tail).
       
      naprotiv(A,B,[_,A,_,B]).
      naprotiv(B,A,[A,_,B,_]).
      naprotiv(B,A,[_,A,_,B]).
       
      sprava(A,B,[A,B,_,_]).
      sprava(A,B,[_,_,A,B]).
       
      sleva(A,B,L):-sprava(B,A,L).
       
      insert(Y,XZ,XYZ):-
        append(X,Z,XZ),append(X,[Y],XY),append(XY,Z,XYZ).

    • Почему member(m(S4,rezchik),L) не имеет такой же конструкции как и предыдущие правила?

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

    #1764

    Задача тривиальна и решается по шаблону (в статье есть 2 похожих переборных задачи). Часть не понятным Вам предикатов уже описаны на блоге с комментариями к каждой строчке. Я не буду повторять это для каждой задачи. Пользуйтесь поиском. Предикаты append, member являются стандартными. Вместо permute можно использовать стандартный permutation.
    Предикат insert тоже не нужен, я бы использовал вместо него стандартный предикат flatten (он тоже описан на блоге).

    Если Вы прочитаете статью внимательно, то заметите, что permutation я не использую. И Вы тоже можете его не использовать, на мой взгляд, код будет понятнее.

Просмотр 4 сообщений - с 1 по 4 (из 4 всего)

Для ответа в этой теме необходимо авторизоваться.