我正在进入Prolog,听说它对于解决逻辑难题非常有用。我发现了一堆(实际上比使用Prolog容易解决的难题)described here之类的逻辑难题。重述:
一个凌乱的孩子写了一个乘法问题。
- 爱丽丝看见了100 x 6。
- 鲍勃看见了101 x 6。
- 丹锯102 x 9。
每个误读数字。该问题的 real 解决方案是什么?
我的第一个想法是定义一个关系,“人在位置看到数字”:
saw(alice, 1, 0).
saw(alice, 0, 1).
saw(alice, 0, 2).
saw(alice, 6, 3).
saw(bob, 1, 0).
saw(bob, 0, 1).
saw(bob, 1, 2).
saw(bob, 6, 3).
saw(dan, 1, 0).
saw(dan, 0, 1).
saw(dan, 2, 2).
saw(dan, 9, 3).
然后一个人可以说一个人,A,如果A在该位置看到了东西,就读错了,而没有错读其他位置:
misread(Person, Digit, Position) :-
saw(Person, Digit, Position),
not(misread(Person, _, not(Position))).
然后正确的数字就是不会被误读的数字:
correct(Digit, Position) :-
not(misread(_, Digit, Position)).
,因此可以通过以下方式读取解决方案:correct(X, Y).
但是,我很难理解我该如何限制每个人都误读了一个问题。关于此事的任何提示将不胜感激。
所有代码组合在一起:
saw(alice, 1, 0).
saw(alice, 0, 1).
saw(alice, 0, 2).
saw(alice, 6, 3).
saw(bob, 1, 0).
saw(bob, 0, 1).
saw(bob, 1, 2).
saw(bob, 6, 3).
saw(dan, 1, 0).
saw(dan, 0, 1).
saw(dan, 2, 2).
saw(dan, 9, 3).
misread(Person, Digit, Position) :-
saw(Person, Digit, Position),
not(misread(Person, _, not(Position))).
correct(Digit, Position) :-
not(misread(_, Digit, Position)).
答案 0 :(得分:1)
我要解决的方法:
saw(alice, 1, 0, 0, 6).
saw(bob, 1, 0, 1, 6).
saw(dan, 1, 0, 2, 9).
首先说明事实。然后,我将对只有一个被误读的事实进行编码:
chk(P, A, B, C, D) :- (saw(P, X, B, C, D), X \= A);
(saw(P, A, X, C, D), X \= B);
(saw(P, A, B, X, D), X \= C);
(saw(P, A, B, C, X), X \= D).
..这有点硬编码“一个错误”。我相信这可以改善。最后是解决方案,指出有4位数字,并在其上方应用“检查”:
digit(A) :- member(A, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).
solve(A,B,C,D) :-
digit(A),
digit(B),
digit(C),
digit(D),
chk(alice, A, B, C, D),
chk(bob, A, B, C, D),
chk(dan, A, B, C, D).
答案 1 :(得分:0)
misread / 3绝望地复杂。具体点,表达别人读过的内容:
misread(Person, Digit, Position) :-
saw(Person, Digit, Position),
saw(Q, D, Position), Q\==Person, D\==Digit,
saw(R, D, Position), R\==Q, R\==Person.
注意:未经测试,我不能说这是否有助于解决难题。
但是应该表达约束,只是观察目标 order 。失败导致的否定-我们在普通Prolog中所拥有的-如果没有具体的数据进行比较就无法工作,然后Q和R上的saw/3
(我们知道我们只有3个实体)将满足随后的否定。实际上,\==
的意思是not(A==B)
-或在现代语法中,\\+(A==B)
。
也许正在尝试将数据流可视化为SQL计划,这是在指示联接顺序。
由于搜索空间太小,编码效率低下可以做到:
misread(Person, Digit, Position) :-
saw(Person, Digit, Position),
saw(Q, D, Position),
saw(R, D, Position),
maplist(\==, [Q,D,R,R],[Person,Digit,Q,Person]).
将所有过滤器放在末尾,这会使锯基数执行(严重)O ^ 3。
切换到dif/2
将使目标顺序无关紧要,并有助于解决性能问题。例如:
misread(Person, Digit, Position) :-
maplist(dif, [Q,D,R,R], [Person,Digit,Q,Person]),
saw(Person, Digit, Position),
saw(Q, D, Position),
saw(R, D, Position).
PS:在看到OP的原始答案后,我未删除此答案(只是一个提示)。
答案 2 :(得分:0)
所以我想出了一个解决问题的方法,现在有了it on CodeReview:
%- Read person saw number at position.
saw(alice, 1, 0).
saw(alice, 0, 1).
saw(alice, 0, 2).
saw(alice, 6, 3).
saw(bob, 1, 0).
saw(bob, 0, 1).
saw(bob, 1, 2).
saw(bob, 6, 3).
saw(dan, 1, 0).
saw(dan, 0, 1).
saw(dan, 2, 2).
saw(dan, 9, 3).
%- Consider the case when two people see one number and one person saw a anoth-
% er number. This doesnt actually mean the person "definitely" misread the nu-
% mber, but if the problem can be solved it measns they definitely did.
definitely_misread(Person, Digit, Position) :-
saw(Person, Digit, Position),
saw(Q, D, Position), Q \== Person, D \== Digit,
saw(R, D, Position), R \== Q, R \== Person.
%- Read a person misread the digit at poisition at position.
misread(Person, Digit, Position) :-
saw(Person, Digit, Position),
not((definitely_misread(Person, D, P), D \== Digit, P \== Position)),
(saw(Q, D1, Position), Q \== Person, D1 \== Digit),
(saw(R, D2, Position), R \== Q, R \== Person, D2 \== Digit).
%- Resolve if the question is actually the correct digit at that position.
correct(Digit, Position) :-
(saw(alice, Digit, Position), not(misread(alice, Digit, Position)));
(saw(bob, Digit, Position), not(misread(bob, Digit, Position)));
(saw(dan, Digit, Position), not(misread(dan, Digit, Position))).
关键点是:
misread
施加进一步的限制(与现在删除的答案有关,请确保其他人不等于misread
组成部分。definitely_misread
来提高效率(不幸的是,没有它,您会得到堆栈错误)。不幸的是,这使它变得格外冗长,这就是为什么我也对CodeReview提出了一个问题。