Решение логической головоломки с помощью Prolog

преступник является одним из A, B, C и D.

говорит: "Это не я"
B говорит: "Это D"
C говорит: "Это B"
D говорит: "Это не я"

и мы знаем, что только один из них говорит правду.

кто? Я хочу решить его с помощью Prolog.

Это вопрос интервью.

5 ответов


один-лайнер решением

?- member(K,[a,b,c,d]),(K\=a->A=1;A=0),(K=d->B=1;B=0),(K=b->C=1;C=0),(K\=d->D=1;D=0),A+B+C+D=:=1.
K = a,
A = 0,
B = 0,
C = 0,
D = 1 ;
false.

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

во-первых, вот его решение как правильное статья:

criminal(K):-
    member(K,[a,b,c,d]),
    (K\=a -> A=1;A=0),
    (K=d  -> B=1;B=0),
    (K=b  -> C=1;C=0),
    (K\=d -> D=1;D=0),
    A+B+C+D=:=1.

и это звучит так:

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

С каждым возможным значением K он пробегает остальную часть предложения. K можно интерпретировать как гипотезу о том, кто преступник. Следующие 4 строки должны предоставить привязки к каждой из переменных A, B, C и D. Вы можете прочитать их следующим образом: при условии, что a - это не преступник, это правдиво иначе не. При условии, что d преступник, б правдив иначе не. АЧС. То есть переменные A, B,... поймать правдивость ответчика индивидуума, учитывая конкретного преступника.

поскольку известным ограничением является тот факт, что только один из них правдив, сумма их истинных значений должна быть 1. При обратном отслеживании Prolog делает следующую привязку для K и проходит через нее снова. Получается ограничение только доволен, если a преступник (и d говорит правду, если я не ошибаюсь). Милый.


вот еще одно решение, которое я нахожу немного менее загадочным, чем Xonix. Протестировано в SWI-Prolog.

% To find a criminal and the truthteller
% 1. Pick a possible criminal
% 2. Pick a possible truthteller and the remaining liars
% 3. Assert that the truthteller's statement is the truth
% 4. Assert that every liar's statement is not the truth
% If both the assertions succeed
% then we have found a criminal and the truthteller.
criminal_and_truthteller(Criminal, Truthteller) :-
    Group = [a, b, c, d],
    member(Criminal, Group),
    select(Truthteller, Group, Liars),
    statement(Truthteller, Criminal, Truth),
    Truth,
    forall(
        member(Liar, Liars),
        (statement(Liar, Criminal, Lie), \+ Lie)
    ).

% Statements
% Arg 1: Who says
% Arg 2: About whom
% Arg 3: Which statement
% e.g. "a claims that a is not a criminal"
statement(a, C, a \= C).
statement(b, C, d  = C).
statement(c, C, b  = C).
statement(d, C, d \= C).

пример использования:

?- criminal_and_truthteller(Criminal, Truthteller).
Criminal = a,
Truthteller = d ;
false.

я столкнулся с этой проблемой и хотел дать ей шанс:

a(K) :- K \== a.
b(d).
c(b).
d(K) :- K \== d.

solve(TruthTeller) :-
    member(K, [a, b, c, d]),
    xor([a(K), b(K), c(K), d(K)], Truth),
    Truth =.. [TruthTeller|_].

xor([Head|Tail], Result) :-
    (   call(Head)
     -> forall(member(X, Tail), \+ call(X)), Result = Head
     ;  xor(Tail, Result)).

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

https://github.com/LogtalkDotOrg/logtalk3/blob/master/examples/puzzles/jam_thief.lgt

Как и решение, опубликованное Kaarel, можно запросить обоснование / объяснение найденного решения.