Логическая задача. Проверка условий в Prolog

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

  • Автор
    Сообщения
  • #2854

    Ksenia
    Участник

    Здравствуйте, нужна помощь с задачей на Visual Prolog.

    В Зазнандии три основных способа передвижения. Можно путешествовать пешком, а можно пользоваться частными воздушными такси или государственными воздушными трамваями. Четверо зазнандцев, живущих на окраине города, регулярно отправляются в центр на работу и на разные развлекательные мероприятия. В соответствии с национальными традициями все четверо одеваются в яркую однотонную одежду.
    Экскурсовод-зазнандец, который использует стиль речи, характерный для жителей страны, объясняет, кто как одевается и кто каким транспортом пользуется.
    1. Из четверых не найдется пары, путешествующей пешком или на воздушном такси.
    2. Ни А ни Б, которые не пользуются воздушным такси, не носят красное.
    3. Тот, кто одевается в красное, не путешествует пешком.
    4. Ни тот, кто одевается в зеленое, ни тот кто одевается в синее, не ездят на такси.
    5. Ни В, который не ездит на воздушном трамвае, ни Г не одевается в оранжевое.
    6. Г, который не одевается в красное, не путешествует пешком.
    7. Единственный, кто одевается в зеленое – а это не Б, не ездит на воздушном трамвае.

    Ниже приведены мои попытки реализации кода, программа выводит “no solution”. В коде не описано первое условие “Из четверых не найдется пары, путешествующей пешком или на воздушном такси” и я не знаю как правильно его описать.

    DOMAINS
      имя,цвет,способ_передвижения = symbol
      количество = integer
      зазнадец = человек(имя,цвет,способ_передвижения)
      список_жителей = зазнадец*
    PREDICATES
      nondeterm member(зазнадец,список_жителей)
      nondeterm возможное_имя(имя)
      nondeterm возможный_цвет(цвет)
      nondeterm возможное_передвижение(способ_передвижения)
      зовут(зазнадец,имя)
      одевает(зазнадец,цвет)
      передвигается(зазнадец,способ_передвижения)
      
      nondeterm зовут_не_Б(зазнадец,список_жителей)
      nondeterm не_одевает_красное(зазнадец,список_жителей)
      nondeterm не_одевает_оранжевое(зазнадец,список_жителей)
      nondeterm не_пользуется_воздушным_такси(зазнадец,список_жителей)
      nondeterm не_пользуется_воздушным_трамваем(зазнадец,список_жителей)
      nondeterm не_путешествует_пешком(зазнадец,список_жителей)
      nondeterm возможное_решение(имя,список_жителей)
    CLAUSES
    %  возможные значения свойств друзей  
      возможное_имя(а).
      возможное_имя(б).
      возможное_имя(в).
      возможное_имя(г).
      возможное_передвижение(пешком).
      возможное_передвижение(воздушное_такси).
      возможное_передвижение(воздушный_трамвай).
      возможный_цвет(красный).
      возможный_цвет(оранжевый).
      возможный_цвет(синий).
      возможный_цвет(зеленый).
      
    %  извлечение значения конкретного свойства 
      зовут(человек(Вася,_,_),Имя):- 
        Имя=Вася.
      одевает(человек(_,Белый,_),Цвет):- 
        Цвет=Белый.
      передвигается(человек(_,_,Автобус),Способ_передвижения):- 
        Способ_передвижения=Автобус.
      %вид_передвижения(человек(_,_,Автобус),Способ_передвижения):- 
        Способ_передвижения=Автобус.
    
    % определение как трактовать
      зовут_не_Б(X,[X|_]).
      не_одевает_красное(X,[X|_]).
      не_одевает_оранжевое(X,[X|_]).
      не_пользуется_воздушным_такси(X,[X|_]).
      не_пользуется_воздушным_трамваем(X,[X|_]).
      не_путешествует_пешком(X,[X|_]).
      
      возможное_решение(Имя,Список_жителей):-
        возможное_имя(Имя1),возможное_имя(Имя2),возможное_имя(Имя3),возможное_имя(Имя4),
        возможный_цвет(Цвет1),возможный_цвет(Цвет2),возможный_цвет(Цвет3),возможный_цвет(Цвет4),
        возможное_передвижение(Способ1),возможное_передвижение(Способ2),возможное_передвижение(Способ3),возможное_передвижение(Способ4),  
        not(Имя1=Имя2),not(Цвет1=Цвет2), not(Способ1=Способ2),
        not(Имя1=Имя3),not(Цвет1=Цвет3), not(Способ1=Способ3),
        not(Имя1=Имя4),not(Цвет1=Цвет4), not(Способ1=Способ4),
        not(Имя2=Имя3),not(Цвет2=Цвет3), not(Способ2=Способ3),
        not(Имя2=Имя4),not(Цвет2=Цвет4), not(Способ2=Способ4),
        not(Имя3=Имя4),not(Цвет3=Цвет4), Способ3=Способ4,
        Жители=[
          человек(Имя1,Цвет1,Способ1),
          человек(Имя2,Цвет2,Способ2),
          человек(Имя3,Цвет3,Способ3),
          человек(Имя4,Цвет4,Способ3)
        ],
    % Ни А ни Б, которые не пользуются воздушным такси, не носят красное.  
        зовут(Зазнадец11,а), не_пользуется_воздушным_такси(Зазнадец11,Жители), не_одевает_красное(Зазнадец11,Жители),
        зовут(Зазнадец12,б), не_пользуется_воздушным_такси(Зазнадец12,Жители), не_одевает_красное(Зазнадец12,Жители),
    % Тот, кто одевается в красное, не путешествует пешком.
        одевает(Зазнадец21,красный), не_путешествует_пешком(Зазнадец21,Жители),
    % Единственный, кто одевается в зеленое - а это не Б, не ездит на воздушном трамвае.
    % Ни тот, кто одевается в зеленое, ни тот кто одевается в синее, не ездят на такси.
        зовут_не_Б(Зазнадец31,Жители), одевает(Зазнадец31,зеленый),
        не_пользуется_воздушным_трамваем(Зазнадец31,Жители),
        не_пользуется_воздушным_такси(Зазнадец31,Жители),
        одевает(Зазнадец32,синий), не_пользуется_воздушным_такси(Зазнадец32,Жители),
    % Ни В, который не ездит на воздушном трамвае, ни Г не одевается в оранжевое.
    % Г, который не одевается в красное, не путешествует пешком.
        зовут(Зазнадец41,в), не_одевает_оранжевое(Зазнадец41,Житель), 
        не_пользуется_воздушным_трамваем(Зазнадец41,Жители), зовут(Зазнадец42,г), 
        не_одевает_оранжевое(Зазнадец42,Житель), не_одевает_красное(Зазнадец42,Жители), 
        не_путешествует_пешком(Зазнадец42,Жители).
     % Из четверых не найдется пары, путешествующей пешком или на воздушном такси.
     %      вид_передвижения(пешком,1),
     %      вид_передвижения(воздушное_такси,1),
     %      вид_передвижения(воздушный_трамвай,2).
            
    GOAL
      возможное_решение(Имя,Список_жителей).
    

    В решении используется предикат member.

    Помогите, пожалуйста, доработать программу.

  • #2862

    Вот эта огромная группа проверок:

    not(Имя1=Имя2),not(Цвет1=Цвет2), not(Способ1=Способ2),
    not(Имя1=Имя3),not(Цвет1=Цвет3), not(Способ1=Способ3),
    not(Имя1=Имя4),not(Цвет1=Цвет4), not(Способ1=Способ4),
    not(Имя2=Имя3),not(Цвет2=Цвет3), not(Способ2=Способ3),
    not(Имя2=Имя4),not(Цвет2=Цвет4), not(Способ2=Способ4),
    not(Имя3=Имя4),not(Цвет3=Цвет4), Способ3=Способ4,

    может быть заменена на
    unique([Имя1, Имя2, Имя3, Имя4]),
    unique([Цвет1, Цвет2, Цвет3, Цвет4]),
    unique([Способ1, Способ2, Способ3, Способ4]),

    Предикат unique можно взять из статьи “Списки в Prolog. Примеры“. Такая замена – более правильный путь, т.к. при копировании и вставке (так получен ваш код) часто возникают ошибки. У вас ошибка в том, что вы требуете, чтобы Способ3 был равен Способ4 (забыли not добавить).

    В остальной части кода, я не понял что именно вы пытались сделать. Все эти предикаты одинаковы:

      зовут_не_Б(X,[X|_]).
      не_одевает_красное(X,[X|_]).
      не_одевает_оранжевое(X,[X|_]).
      не_пользуется_воздушным_такси(X,[X|_]).
      не_пользуется_воздушным_трамваем(X,[X|_]).
      не_путешествует_пешком(X,[X|_]).

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

  • #2937

    В разделе domains описываем сначала типы перечислений – способы перемещения, цвета и имена, затем добавляем структуру персонажа. Гипотеза (то, что предполагает программа), в этом случае, представляет собой список персонажей. Кроме того, при выборе тех, кто одевает зеленое в нашей программе появится список имен, поэтому он тоже должен быть описан:

    domains
      тип_способ_перемещения = pes; tax; tram
      тип_цвет_одежды = red; green; blue; orange
      тип_имя = ma; mb; mc; md
      
      тип_структура_зазнадец = zaz(тип_имя, тип_цвет_одежды, тип_способ_перемещения)
      
      тип_гипотеза = тип_структура_зазнадец*
      
      тип_имена = тип_имя*

    В разделе predicates нужно описать все функции, которые нам потребуются:

    predicates
      nondeterm способ_перемещения(тип_способ_перемещения)
      nondeterm цвет_одежды(тип_цвет_одежды)
      nondeterm имя(тип_имя)
      
      nondeterm генерация_гипотезы(тип_гипотеза)
      nondeterm проверка_гипотезы(тип_гипотеза)
      
      nondeterm входит_в(тип_структура_зазнадец, тип_гипотеза)
      nondeterm входит_в(тип_имя, список_имен)
    
      nondeterm есть_цвет_способ(тип_цвет_одежды, тип_способ_перемещения, тип_гипотеза)
      nondeterm есть_имя_способ(тип_имя, тип_способ_перемещения, тип_гипотеза)
      nondeterm есть_имя_цвет(тип_имя, тип_цвет_одежды, тип_гипотеза)
      
      nondeterm проверка_есть_пара_способ_передвижения(тип_гипотеза, тип_способ_перемещения)
      
      nondeterm проверка_имя_красное_ИЛИ_пешком(тип_имя, тип_гипотеза)

    Теперь можно приступать к реализации функций в разделе clauses. Первые три предиката нужны для организации перебора вариантов. Обращаясь к функции программа должна получать различные варианты имен, цветов и т.п.:

    clauses
      имя(ma).
      имя(mb).
      имя(mc).
      имя(md).
      
      цвет_одежды(red).
      цвет_одежды(green).
      цвет_одежды(blue).
      цвет_одежды(orange).
      
      способ_перемещения(pes).
      способ_перемещения(tax).
      способ_перемещения(tram).

    Предикат входит_в в этой программе полностью эквивалентен предикату member.

    Генерация гипотезы сводится к построению списка из четырех элементов. Имена персонажей можно сразу фиксировать и запросить лишь генерацию цветов и способов перемещения:

      генерация_гипотезы([
        zaz(ma, ЦветА, СпособА),
        zaz(mb, ЦветБ, СпособБ),
        zaz(mc, ЦветВ, СпособВ),
        zaz(md, ЦветГ, СпособГ)
      ]):-
        цвет_одежды(ЦветА), цвет_одежды(ЦветБ), цвет_одежды(ЦветВ), цвет_одежды(ЦветГ),
        
        /*
        NOT(ЦветА = ЦветБ), NOT(ЦветА = ЦветВ), NOT(ЦветА = ЦветГ), 
        NOT(ЦветБ = ЦветВ), NOT(ЦветБ = ЦветГ),
        NOT(ЦветГ = ЦветВ),
        */
        
        способ_перемещения(СпособА), способ_перемещения(СпособБ), 
        способ_перемещения(СпособВ), способ_перемещения(СпособГ).

    В приведенном фрагменте закомментирован блок кода, выполняющий проверку того, что цвета не повторяются, т.к. в условии это требование сформулировано размыто.

    Следующие три предиката проверяют наличие в гипотезе человека с двумя из трех нужных параметров. При программировании на SWI Prolog, например, от них можно было бы избавиться, но в Visual Prolog оператор NOT не может применяться к запросам, содержащим анонимную переменную. Конструкция NOT(входит_в(zaz(ma, _Цвет, tax), Гипотеза)) привела бы к ошибке компиляции, т.к. мы передаем несвязанную переменную _Цвет оператору NOT. Для решения проблемы нужно написать предикаты, принимающие только два параметра и скрывающие лишнюю переменную от NOT:

    есть_цвет_способ(Цвет, Способ, Гипотеза):-
      	входит_в(zaz(_Имя, Цвет, Способ), Гипотеза).
      	
      есть_имя_способ(Имя, Способ, Гипотеза):-
        	входит_в(zaz(Имя, _Цвет, Способ), Гипотеза).
      	
      есть_имя_цвет(Имя, Цвет, Гипотеза):-
       	входит_в(zaz(Имя, Цвет, _Способ), Гипотеза).

    Следующие предикаты проверяют выполнение конкретных условий задачи:

    проверка_есть_пара_способ_передвижения(Гипотеза, Способ):-
    	входит_в(zaz(Имя1, _Цвет1, Способ), Гипотеза), 
    	входит_в(zaz(Имя2, _Цвет2, Способ), Гипотеза),
    	NOT(Имя1 = Имя2).
    

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

    проверка_имя_красное_ИЛИ_пешком(Имя, Гипотеза):-
      	входит_в(zaz(Имя, _Цвет, pes), Гипотеза);
      	входит_в(zaz(Имя, red, _Способ), Гипотеза).

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

    Проверка гипотезы собирает все описанные выше предикаты в одно высказывание чтобы обеспечить выполнение всех условий задачи:

      проверка_гипотезы(Гипотеза):-
      	NOT(проверка_есть_пара_способ_передвижения(Гипотеза, pes)),
      	NOT(проверка_есть_пара_способ_передвижения(Гипотеза, tax)),
      	
     	NOT(есть_имя_способ(ma, tax, Гипотеза)),
     	NOT(есть_имя_цвет(ma, red, Гипотеза)),
     	NOT(есть_имя_способ(mb, tax, Гипотеза)),
     	NOT(есть_имя_цвет(mb, red, Гипотеза)),
     	
     	NOT(есть_цвет_способ(red, pes, Гипотеза)),
     	
     	NOT(есть_цвет_способ(green, tax, Гипотеза)),
        	NOT(есть_цвет_способ(blue, tax, Гипотеза)),
     	
     	findall(Имя, входит_в(zaz(Имя, green, _Способ), Гипотеза), ОдеваютЗеленое),
      	ОдеваютЗеленое = [ЕдинственныйОдеваетЗеленое],
      	NOT(ЕдинственныйОдеваетЗеленое = mb),
      	NOT(есть_имя_способ(ЕдинственныйОдеваетЗеленое, tram, Гипотеза)),
     	
     	NOT(есть_имя_способ(mc, tram, Гипотеза)),
        	%NOT(есть_имя_цвет(mc, orange, Гипотеза)),
        	NOT(есть_имя_цвет(md, orange, Гипотеза)),
     	
    	NOT(проверка_имя_красное_ИЛИ_пешком(md, Гипотеза)).

    В секции goal остается лишь запустить генерацию гипотезы и последующую ее проверку:

    goal
     генерация_гипотезы(Гипотеза),
     
     проверка_гипотезы(Гипотеза).

    На скриншоте показан пример работы программы.

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