我最近开始学习Prolog的乐趣。我发现以下murder mystery puzzle。由于除了基本知识之外,我对Prolog的了解不多,所以我无法真正评估链接中提供的解决方案,但是,对我来说,它似乎并不是特别好。我的解决方案不足以产生正确的答案,因此我正在寻找一些有关如何到达那里的指示,或者是否有可能通过我的方法到达那里。万一链接断开,这是一个难题:
要发现谁杀了博迪先生,您需要了解每个人在哪里 是,房间里有什么武器。线索散布在各处 测验(您必须先读完所有10个知识,才能解决问题1)。
开始之前,您需要了解犯罪嫌疑人。一共有三个人(乔治, 约翰·罗伯特(John,Robert)和三个女人(芭芭拉(Barbara),克里斯汀(Christine),约兰达(Yolanda)。每 一个人在另一个房间(浴室,餐厅,厨房,起居室) 房间,餐具室,书房)。在每个房间里发现了可疑武器(Bag, 火器,汽油,刀,毒药,绳索)。谁在厨房里找到的?
提示1:找不到用绳子,刀子或其他东西在厨房里的男人。 袋。那么,在枪械中发现了哪种武器而非枪支 厨房?
提示2:芭芭拉要么在书房里,要么在洗手间里。尤兰达原为 在另一个。芭芭拉在哪个房间里找到了?
提示3:不是Barbara也不是George的那个提包的人是 不在浴室或饭厅。谁在房间里的书包 和他们在一起?
线索4:在研究中发现了有绳子的女人。谁有 绳子?
第5条线索:客厅中的武器是与约翰或 乔治。客厅里有什么武器?
提示6:这把刀不在饭厅里。那把刀在哪里?
提示7:Yolanda没有使用研究中发现的武器,也没有使用 储藏室。尤兰达发现了什么武器?
线索8:枪支和乔治一起在房间里。在哪个房间 找到枪支了?
人们发现博迪先生在储藏室里被毒气了。犯罪嫌疑人 在那个房间里发现的是凶手。那谁给你指出 手指朝?
这是作者解决方案的link。
这是我尝试的解决方案:
male(george).
male(john).
male(robert).
female(barbara).
female(christine).
female(yolanda).
person(X) :- male(X).
person(X) :- female(X).
room(kitchen).
room(bathroom).
room(diningroom).
room(livingroom).
room(pantry).
room(study).
weapon(bag).
weapon(firearm).
weapon(gas).
weapon(knife).
weapon(poison).
weapon(rope).
/*
Clue 1: The man in the kitchen was not found with
the rope, knife, or bag.
Which weapon, then, which was not the firearm,
was found in the kitchen?
*/
/* X is Weapon, Y is Room, Z is Person */
killer(X, Y, Z) :-
room(Y) = room(kitchen),
male(Z),
dif(weapon(X), weapon(rope)),
dif(weapon(X), weapon(knife)),
dif(weapon(X), weapon(bag)),
dif(weapon(X), weapon(firearm)).
/*
Clue 2: Barbara was either in the study or the bathroom;
Yolanda was in the other.
Which room was Barbara found in?
*/
/* It was easy to deduce the following from other data */
killer(X, Y, Z) :-
female(Z) = female(barbara),
room(study) = room(Y).
killer(X, Y, Z) :-
female(Z) = female(yolanda),
room(bathroom) = room(Y).
/*
Clue 3: The person with the bag, who was not Barbara nor
George, was not in the bathroom nor the dining room.
Who had the bag in the room with them?
*/
killer(X, Y, Z) :-
weapon(bag) = weapon(X),
dif(room(Y), room(bathroom)),
dif(room(Y), room(diningroom)),
dif(person(Z), male(george)),
dif(person(Z), female(barbara)).
/*
Clue 4: The woman with the rope was found in the study.
Who had the rope?
*/
killer(X, Y, Z) :-
weapon(rope) = weapon(X),
room(study) = room(Y),
female(Z).
/*
Clue 5: The weapon in the living room was found with either
John or George. What weapon was in the living room?
*/
killer(X, Y, Z) :-
room(Y) = room(livingroom),
dif(male(Z), male(robert)).
/*
Clue 6: The knife was not in the dining room.
So where was the knife?
*/
killer(X, Y, Z) :-
weapon(knife) = weapon(X),
room(Y) \= room(diningroom).
/*
Clue 7: Yolanda was not with the weapon found
in the study nor the pantry.
What weapon was found with Yolanda?
*/
killer(X, Y, Z) :-
female(yolanda) = female(Z),
dif(room(study), room(Y)),
dif(room(pantry), room(Y)).
/*
Clue 8: The firearm was in the room with George.
In which room was the firearm found?
*/
killer(X, Y, Z) :-
weapon(firearm) = weapon(X),
male(george) = male(Z).
/*
It was discovered that Mr. Boddy was gassed in the pantry.
The suspect found in that room was the murderer.
Who, then, do you point the finger towards?
*/
killer(X, Y, Z) :-
room(Y) = room(pantry),
weapon(X) = weapon(gas).
答案 0 :(得分:4)
我对这个问题采取了更为积极的态度。与其尝试任何形式的否定,我都只是简单地统一了。
关键是这个谓词对:
members([],_).
members([M|Ms],Xs) :- select(M,Xs,Ys),members(Ms,Ys).
这是一个基本的排列谓词。它将获取第一个参数的列表,并尝试统一第二个列表的所有排列。
现在很多规则变得很容易表达:
例如,线索1:
clue1(House) :- members([[P,kitchen,_],[_,_,rope],[_,_,knife],[_,_,bag],[_,_,firearm]],House),man(P).
因此,这意味着rope
,knife
,bag
和firearm
都是房子的成员,但与kitchen
位于不同的房间。 Prolog会继续追踪,直到找到适合这些项目为止。
这是我的完整解决方案:
man(george).
man(john).
man(robert).
woman(barbara).
woman(christine).
woman(yolanda).
members([],_).
members([M|Ms],Xs) :- select(M,Xs,Ys),members(Ms,Ys).
clue1(House) :- members([[P,kitchen,_],[_,_,rope],[_,_,knife],[_,_,bag],[_,_,firearm]],House),man(P).
clue2(House) :- member([barbara,study,_],House), member([yolanda,bathroom,_],House).
clue2(House) :- member([barbara,bathroom,_],House), member([yolanda,study,_],House).
clue3(House) :- members([[_,_,bag],[barbara,_,_],[george,_,_]],House),members([[_,_,bag],[_,bathroom,_],[_,dining_room,_]],House).
clue4(House) :- members([[P,study,rope]],House),woman(P).
clue5(House) :- members([[john,living_room,_]],House).
clue5(House) :- members([[george,living_room,_]],House).
clue6(House) :- members([[_,_,knife],[_,dining_room,_]],House).
clue7(House) :- members([[yolanda,_,_],[_,study,_],[_,pantry,_]],House).
clue8(House) :- member([george,_,firearm],House).
clue9(House,P) :- members([[P,pantry,gas]],House).
solve(X) :-
House = [[_,bathroom,_],[_,dining_room,_],[_,kitchen,_],[_,living_room,_],[_,pantry,_],[_,study,_]],
clue1(House),
clue2(House),
clue3(House),
clue4(House),
clue5(House),
clue6(House),
clue7(House),
clue8(House),
clue9(House,X),
members([[george,_,_],[john,_,_],[robert,_,_],[barbara,_,_],[christine,_,_],[yolanda,_,_]],House),
members([[_,_,bag],[_,_,firearm],[_,_,gas],[_,_,knife],[_,_,poison],[_,_,rope]],House),
write(House),
true.
那给了我
?- solve(X).
[[yolanda,bathroom,knife],[george,dining_room,firearm],[robert,kitchen,poison],[john,living_room,bag],[christine,pantry,gas],[barbara,study,rope]]
X = christine .
答案 1 :(得分:1)
编辑:在https://swish.swi-prolog.org/p/crime_constraints.pl上查看参考解决方案的改进版本。
我同意您链接的解决方案很丑陋,但确实使用了正确的方法。您的方向不正确。一些说明:
/* X is Weapon, Y is Room, Z is Person */
为什么不使用变量名Weapon
,Room
和Person
呢?它使您的程序更易于阅读。
weapon(rope) = weapon(X)
这完全等同于只写X = rope
或rope = X
。
但是除了这些之外,您解决这个难题的方式还有另外两个大问题:
首先,您没有将对象之间的关系建模为数据。例如,对于“在研究中发现有绳子的女人”。你有这个条款:
killer(X, Y, Z) :-
weapon(rope) = weapon(X),
room(study) = room(Y),
female(Z).
这确实具有三个解决方案,您可以将它们解释为“关系killer(rope, study, barbara)
,killer(rope, study, christine)
或killer(rope, study, yolanda)
”,但是您的程序却没有知道如何用这种方式解释它。您实际上并没有构造表达这种关系的数据。这就是您链接到的解决方案正确执行的操作:它将房间和武器建模为变量,可以绑定到代表人的原子上。因此,它可以将此线索表示为woman(Rope)
(“有绳子的人是女人”)和Rope = Study
(“绳子和书房是同一个人”)。
第二个大问题是您将所有线索建模为 same 谓词的不同子句。这是错误的,因为在Prolog中谓词的不同子句表示 choice :如果第一个子句包含或,则第二个子句包含或第三个子句成立,依此类推。但是,您想表达第一个线索持有和,第二个线索持有和,第三个线索成立,等等。通过将 one 子句主体中的不同条件与,
组合在一起。这就是链接的解决方案具有不同谓词clue1
,clue2
等的原因,所有这些谓词都是从一个大谓词的主体中调用的。
答案 2 :(得分:0)
从线索中得出规则
每个人都在不同的房间(浴室,餐厅,厨房, 客厅,厨房,书房)。在每个房间里发现可疑武器 (袋子,枪支,汽油,刀,毒药,绳索)。
unique(A,B,C,D,E,F) :-
A \= B, A \= C, A \= D, A \= E, A \= F,
B \= C, B \= D, B \= E, B \= F,
C \= D, C \= E, C \= F,
D \= E, D \= F,
E \= F.
suspicious(pwr(george,WA,RA), pwr(john,WB,RB), pwr(robert,WC,RC), pwr(barbara,WD,RD), pwr(christine,WE,RE), pwr(yolanda,WF,RF)) :-
weapon(WA), weapon(WB), weapon(WC), weapon(WD), weapon(WE), weapon(WF),
unique(WA,WB,WC,WD,WE,WF),
room(RA), room(RB), room(RC), room(RD), room(RE), room(RF),
unique(RA,RB,RC,RD,RE,RF).
现在让我们检查
提示1:找不到用绳子,刀子或其他东西在厨房里的男人。 袋。那么,在枪械中发现了哪种武器而非枪支 厨房?
clue1(L) :-
oneof(pwr(P,W,kitchen),L),
male(P),
weapon(W),
W \= rope, W \= knife, W \= bag, W \= firearm.
我们针对8条线索中的每条线索执行此操作,最后
人们发现博迪先生在储藏室里被毒气了。犯罪嫌疑人 在那个房间里发现的是凶手。那谁给你指出 手指朝?
killer(X, L) :- member(pwr(X,gas,pantry),L).
resolved(X) :-
suspicious(A,B,C,D,E,F),
L = [A,B,C,D,E,F],
clue1(L),
clue2(L),
clue3(L),
clue4(L),
clue5(L),
clue6(L),
clue7(L),
clue8(L),
killer(X, L).
完整程序可以为found和run。推论相当慢(但比作者的解决方案要快)。
为什么使用关系而不是变量绑定是一种更好的设计?
我了解Prolog程序是获取知识的规则集。这意味着:
并非在参考解决方案中每个方面都是最优的,如果对Prolog更为熟悉,则可能会更好地表达某些方面。
为什么我认为规则集应该对世界变化具有鲁棒性?
我在程序分析中使用了数据记录。这意味着源代码(或字节码)中的每个关系都被建模为事实,并且规则推断出类型,安全漏洞,设计模式等。存在数百万个事实和数千个规则集代码。添加实体(例如,源代码行,类型注释)不应驱使我重新实现规则集代码(很难正确地编写它)。
为什么我认为使用隐式关系是不好的代码?
考虑reference solution中的这段代码,这完全是一种误导:
clue1(Bathroom, Dining, Kitchen, Livingroom, Pantry, Study, Bag, Firearm, Gas, Knife, Poison, Rope) :-
man(Kitchen), // a man is a kitchen?
\+Kitchen=Rope, // a kitchen is not a rope?
\+Kitchen=Knife, // a kitchen is not a knife?
\+Kitchen=Bag, // a kitchen is not a bag
\+Kitchen=Firearm. // a kitchen is not a firearm
好吧,变量名很难看,可读性更好
clue1(InBathroom, InDiningroom, InKitchen, InLivingroom, InPantry, InStudy, WithBag, WithFirearm, WithGas, WithKnife, WithPoison, WithRope) :-
man(InKitchen), // (person) in the kitchen is a man - ok
\+Kitchen=Rope, // (person) in the kitchen is not
(person) with a rope - better than above
\+Kitchen=Knife, // ...
\+Kitchen=Bag, // ...
\+Kitchen=Firearm. // ...
但是我们将等式关系误用为显式关系。有一个明确的指示:名称中包含谓词的变量可能是隐式关系。 “ personInKitchen”是连接两个实体“ person”和“ kitchen”的(逻辑)谓词“ in”。
与之相比,带有列表和功能符号的模型(suspect/3
是将人连接到武器和房间的关系功能,Suspects
是嫌疑犯的列表):
clue1(Suspects) :-
member(suspect(Person,Weapon,Room),Suspects),
male(Person), // The man (Person)
Room = kitchen, // in the Kitchen (Room)
Weapon \= rope, // was not found with the (Weapon) rope
Weapon \= knife, // (Weapon) knife
Weapon \= bag, // (Weapon) bag
Weapon \= firearm.// (Weapon) firearm
摘要
因此,如果您出于个人目的使用prolog,我不介意“滥用”变量来快速解决问题。但是,如果您的规则集和数据不断增长,在我看来,显式建模所有关系就非常重要。