なんか Prolog で解きやすそうな問題が転がっていたので解いてみる。
問題文
引用元: http://anago.2ch.sc/test/read.cgi/ghard/1437182679/#ghard/1437182679/218
Prolog による解法
コード
- 2015/07/21: 考慮漏れがあったので修正しました。なお、結果は変わりませんでした。
count(List, X, Count) :- count_sub(List, X, 0, Count).
count_sub([], _, N, N).
count_sub([Head | Xs], X, N, Count) :-
(Head == X -> N1 is N + 1; N1 is N),
count_sub(Xs, X, N1, Count).
% トランプの数は1〜13まで。
card(X) :- between(1, 13, X).
% さやかのヒントにあてはまる(かつ、CardAはCardBより小さい)。
is_satisfy_sayaka_said(CardA, CardB) :-
card(CardA),
card(CardB),
CardA =\= 1,
CardB =\= 1,
CardA =\= CardB,
CardB mod CardA =:= 0.
% 相手の数字を確定できる。
can_guess(WinnerCard, AllCandidates) :-
flatten(AllCandidates, AllCandidateCards),
card(WinnerCard),
count(AllCandidateCards, WinnerCard, 1).
% どちらのカードからも、相手の数字を確定できない。
cannot_eiter_guess(CardA, CardB, AllCandidates) :-
card(CardA), card(CardB),
not(can_guess(CardA, AllCandidates)),
not(can_guess(CardB, AllCandidates)).
% カードの組は、解候補に含まれる。
is_candidate(CardA, CardB, AllCandidates) :-
member([CardA, CardB], AllCandidates);
member([CardB, CardA], AllCandidates).
% 以下の条件を満たす解である。
% (1) さやかのヒントにあてはまる
% (2) どちらも相手の数字を確定できない
% (3) (2)を知ると、どちらかが相手の数字を確定できる
is_answer(WinnerCard, LoserCard) :-
findall([CardA, CardB], (
is_satisfy_sayaka_said(CardA, CardB)
), CandidatesSatisfySayakaSaid),
findall([CardA, CardB], (
% (1)
is_satisfy_sayaka_said(CardA, CardB),
% (2)
cannot_eiter_guess(CardA, CardB, CandidatesSatisfySayakaSaid)
), CandidatesWhenEitherCannotGuess),
% (3)
can_guess(WinnerCard, CandidatesWhenEitherCannotGuess),
is_candidate(WinnerCard, LoserCard, CandidatesWhenEitherCannotGuess).
基本的に英語がひどいのはお許しください。
結果
?- is_answer(CardA, CardB, Minami).
CardA = Minami, Minami = 10,
CardB = 2 ;
false.
みなみさんが10、かおりさんが2のようですね。
結論
やっぱり Prolog は最高だぜ!
(英語・コードのご指摘大歓迎です)