在练习中我试图在Prolog中编写一个应该帮助警察找到杀手的程序。我的代码如下:
student(alexander).
student(tobias).
student(michael).
student(peter).
passed(alexander,chemistry).
passed(peter,history).
passed(tobias,maths).
passed(michael,english).
failed(alexander,english).
failed(peter,chemistry).
failed(tobias,chemistry).
failed(alexander,chemistry).
passed(X, maths) :- failed(X, english), failed(X, history).
passed(X, english) :- failed(X, history), passed(X,maths).
passed(X, economics) :- passed(X, english), failed(X, history).
failed(X, history) :- failed(X, economics), passed(X, chemistry).
failed(X, english) :- failed(X, chemistry), passed(X, history).
killer(X):-student(X),failed(X,economics).*
确定了以下事实:
四名学生中有一人杀死了他的教授。当体检医师检查身体时,他右手发现了一张纸:“ 杀手经济失败“。
我现在如何将传递给失败?
答案 0 :(得分:1)
我对你的陈述有一些看法。
首先,您的谓词english/1
,maths/1
不足以表达特定学生通过考试的事实。如果你断言english(failed)
,那么这句话的学生是什么?您需要将学生包含在关系中(例如english(michael, passed)
)。
此外,我建议您将failed
或passed
结果视为关系。您可以使用两个此类关系failed/2
和passed/2
,其中failed(S, E)
表示学生S
检查失败E
。 E.g:
∀ X : student(X) ∧ failed(X, english) ∧ failed(X, history) → passed(X, maths)
将在Prolog中部分表示如下:
passed(X, maths) :- student(X), failed(X, english), failed(X, history).
当然,如果student(X)
的第一个参数始终是学生,则可以删除failed/2
条件。
现在,句子
每个失败的英语和历史的学生都通过了数学。
可以在FOL中正式化如下:
∀ X : student(X) ∧ failed(X, english) ∧ failed(X, history) → passed(X, maths)
使用A → B ≡ ¬A ∨ B
,您会找到等效的表单:
∀ X : ¬student(X) ∨ ¬failed(X, english) ∨ ¬failed(X, history) ∨ passed(X, maths)
(以析取范式),或回到暗示:
∀ X : ¬passed(X, maths) ∧ failed(X, english) ∧ failed(X, history) → ¬student(X)
∀ X : student(X) ∧ ¬passed(X, maths) ∧ failed(X, history) → ¬failed(X, english)
∀ X : student(X) ∧ failed(X, english) ∧ ¬passed(X, maths) → ¬failed(X, history)
小心,Prolog使用closed world assumption(¬student(X)
不是\+ student(X)
),因此您无法直接将上述规则翻译为Prolog。
假设每个学生都参加了所有考试并使用∀ X : ∀ E : passed(X, E) ⟺ ¬failed(X, E)
,您可以推断出以下条款:
∀ X : student(X) ∧ failed(X, maths) ∧ failed(X, history) → passed(X, english)
∀ X : student(X) ∧ failed(X, english) ∧ failed(X, maths) → passed(X, history)
所以,从那个短语(每个学生都失败了英语和历史通过数学),你应该在Prolog中提取3个单独的规则:
passed(X, maths) :- student(X), failed(X, english), failed(X, history).
passed(X, english) :- student(X), failed(X, maths), failed(X, history).
passed(X, history) :- student(X), failed(X, english), failed(X, maths).
如果不是所有学生参加所有考试的情况,你应该引入一些谓词tooks(Student, Exam)
,但上面的解释会变得更复杂。
严格地说,代表性,另一个变体是使用具有以下含义的单个谓词exam_result/3
:exam_result(Student, Exam, Result)
表示学生Student
参加考试Exam
及其结果是Result
。
result(alexander, chemistry, passed).
我们的想法是不要将上述表述建议作为规则。
第二个观察,您使用单个谓词killer/3
来表示大量信息。
killer(alexander,english(failed),chemistry(passed)).
将它分成更独立,更清晰的关系更为直观:
killer(alexander).
failed(alexander, english).
passed(chemistry, alexander).
更容易检查特定学生是否通过或未通过特定考试。此外,如果学生的考试次数多于(或少于两次),则很难扩展killer/3
谓词(以及使用该谓词的所有规则)。
要找到未通过特定考试(例如经济学)的学生,您可以询问Prolog:
?- failed(Student, economics).
Student=alexander ;
...
答案 1 :(得分:0)
将问题视为CSP
解决这个难题的另一种方法是将其视为约束满足问题。 CSP由一组变量定义,每个变量的域和一组表示这些变量之间关系的约束。解决这样的问题意味着从其域中为每个变量分配一个值,以便满足所有约束。
在这种情况下,可能的表示是将每个学生考试对视为可以采用以下两个值之一的变量:[passed, failed]
。约束将完全是问题中给出的陈述。在Prolog中表示解决方案的一种可能性是使用包含结构Exams
的列表exam(Student, Exam, Result)
(例如Exams = [exam(alexander, history, passed), exam(tobias, maths, failed), ...]
)。这个列表对每个学生考试对都只有一个元素。
在一种天真的方法中,依次生成所有可能的解决方案并根据约束进行检查。
检查约束
现在,要检查限制为亚历山大失败英语是微不足道的(使用member/2
)。检查是否
每个失败的英语和历史的学生都通过了数学。
你可能需要重新制定它:
如果学生的英语和历史都没有通过并且没有通过数学,则情况并非如此。
了解为什么这两者是等价的:
∀ X : failed(X, english) ∧ failed(X, history) → passed(X, maths)
≡
∀ X : ¬failed(X, english) ∨ ¬failed(X, history) ∨ passed(X, maths)
≡
¬( ¬(∀ X : ¬failed(X, english) ∨ ¬failed(X, history) ∨ passed(X, maths)))
≡
¬( ∃ X : failed(X, english) ∧ failed(X,history) ∧ ¬passed(X, maths))
假设每个学生都参加了所有考试并使用∀ X : ∀ E : passed(X, E) ⟺ ¬failed(X, E)
:
¬( ∃ X : failed(X, english) ∧ failed(X,history) ∧ failed(X, maths))
这最后一种形式可以很容易地转化为候选解决方案的约束:
\+ ( member(exam(X, english, failed), Exams),
member(exam(X, history, failed), Exams),
member(exam(X, maths, failed), Exams) )
稍后编辑: 对于CSP来说,这是不是一种有效的方法(有更好的算法库和启发式算法:前向检查,AC3,值排序,变量排序等),但它代表了一个良好的开端。
答案 2 :(得分:0)
这是我对这个很好的谜题的看法,但我完全重写了代码。 请注意,您的问题仍然存在一些不一致,例如
passed(alexander,chemistry).
...
failed(alexander,chemistry).
无论如何,Prolog中的相互递归规则很容易导致非终止程序,因此“真实表示”更好地处理数据,而不是规则...
:- module(killer, [killer/1, possible_killer/1]).
killer(S) :-
student(S, Facts),
sure(Facts, Results),
compatible(-economics, Results).
possible_killer(S) :-
student(S, Facts),
sure(Facts, Results),
% memberchk(-economics, Results).
\+ memberchk(+economics, Results).
student(alexander, [-english, +chemistry]). % Alexander failed English but passed chemistry.
student(peter, [-chemistry, +history]). % Peter failed chemistry and passed history.
student(tobias, [-history, +maths]). % Tobias failed history and passed maths.
student(michael, [-maths, +english]). % Michael failed maths and passed English.
rule([-english, -history], +maths ). % Every student that failed both English and History, passed Maths.
rule([-history, +maths], +english ). % Every student that failed history but passed math, passed English.
rule([-history, +english], +economics). % Every student that failed history but passed English, passed economics.
rule([-economics, +chemistry], -history). % Every student that failed economics but passed chemistry, failed history.
rule([-chemistry, +history], -english). % Every student that failed chemistry but passed history, failed English.
sure(Facts, Results) :-
rule(IF, THEN),
forward(IF, THEN, Facts, Update),
!, sure(Update, Results).
sure(Facts, Facts).
forward(Conditions, Consequence, Facts, [Consequence|Facts]) :-
\+ memberchk(Consequence, Facts),
forall(member(C, Conditions), memberchk(C, Facts)).
invert(+C, -C).
invert(-C, +C).
compatible(R, Results) :-
invert(R, I),
\+ memberchk(I, Results),
rule(Conditions, Fact),
memberchk(R, Conditions),
forall(member(C, Conditions), compatible(Fact, [C|Results])).
compatible(R, Results) :-
memberchk(R, Results).
结果:
?- possible_killer(X).
X = alexander ;
X = peter ;
X = michael.
“积极”的知识足以排除托比亚斯。 我们需要一种更好的方式来探索“负面”知识,正如都铎指出的那样,在Prolog中可能会有问题需要评估。
?- killer(X).
X = michael ;
false.