用prolog解爱因斯坦的智力题
昨天刚学了prolog,解决了几个简单的逻辑问题,就想用prolog来解决人脑不容易解决的问题,于是想到传说中的爱因斯坦出的智力题。对于这种比较复杂的逻辑问题来说,在概念不清楚,逻辑学基础不够强的情况下,用prolog解决起来其实并不简单,犯了很多概念和逻辑上的错误,花了一天功夫终于会解了。
这就是传说中爱因斯坦出的,号称全世界只有2%的人能解出的逻辑题:
------------------------------------------------
1、在一条街上,有5座房子,喷了5种颜色。
2、每个房里住着不同国籍的人
3、每个人喝不同的饮料,抽不同品牌的香烟,养不同的宠物
问题是:谁养鱼?
提示:
1、英国人住红色房子
2、瑞典人养狗
3、丹麦人喝茶
4、绿色房子在白色房子左面
5、绿色房子主人喝咖啡
6、抽PallMall香烟的人养鸟
7、黄色房子主人抽Dunhill香烟
8、住在中间房子的人喝牛奶
9、挪威人住第一间房
10、抽Blends香烟的人住在养猫的人隔壁
11、养马的人住抽Dunhill香烟的人隔壁
12、抽BlueMaster的人喝啤酒
13、德国人抽Prince香烟
14、挪威人住蓝色房子隔壁
15、抽Blends香烟的人有一个喝水的邻居
我们用列表
[h(N1,C1,D1,P1,S1),h(N2,C2,D2,P2,S2),h(N3,C3,D3,P3,S3),h(N4,C4,D4,P4,S4),h(N5,C5,D5,P5,S5)]
来表示房子列表。房子列表中的元素h(N,C,D,P,S)表示一个房子,N,C,D,P,S分别表示房子的属性:主人的国籍,颜色,主人所喝的饮料,养的宠物,抽的烟。
然后定义如下的谓词:
nation(h(N,_,_,_,_),N).表示判断房间主人的国籍。
color(h(_,C,_,_,_),C).表示判断房间的颜色。
drink(h(_,_,D,_,_),D).表示判断房间主人所喝的饮料。
pet(h(_,_,_,P,_),P).表示判断房间主人所养的宠物。
smoke(h(_,_,_,_,S),S).表示判断房间主人所抽的烟。
定义题目中要用到一些关于位置的谓词:
left(HA,HB,L) :- append(L1,L2,L),append(L3,[HA,HB],L1).
定义的是:在房子列表L中,房子HA在HB的左边。用的是分治定义:将L分为两个子表L1,L2,如果L1等于表[HA,HB]连接在某一个表L3的末尾所构成的新表,那么HA就在HB的左边。
neighbor(HA,HB,L) :- left(HA,HB,L).
neighbor(HA,HB,L) :- left(HB,HA,L).
利用前面定义的left谓词来定义neighbor谓词。
center(H,[_,_,H,_,_]).
first(H,[H,_,_,_,_]).
定义center和first谓词,分别表示房屋H在房子列表的中间和房子列表的最左边。
定义部分完成,以下是解决问题部分。
将要解决的问题标记为einsten(X),X是需要求的参数,将它与房子列表绑定:
X = [h(N1,C1,D1,P1,S1),h(N2,C2,D2,P2,S2),h(N3,C3,D3,P3,S3),h(N4,C4,D4,P4,S4),h(N5,C5,D5,P5,S5)],
以下是对条件的描述:
注:member(H,X)表示判断房子H是否在房子列表X中,或者从房子列表X中取一间房子放在H中。
% 英国人住红色房子
member(H1,X),nation(H1,englishman),color(H1,red),
% 瑞典人养狗
member(H2,X),nation(H2,swede),pet(H2,dog),
% 丹麦人喝茶
member(H3,X),nation(H3,dane),drink(H3,tea),
% 绿色房子在白色房子左面
member(H4a,X),member(H4b,X),color(H4a,green),color(H4b,white),left(H4a,H4b,X),
% 绿色房子主人喝咖啡
member(H5,X),color(H5,green),drink(H5,coffee),
%抽Pall Mall 香烟的人养鸟
member(H6,X),smoke(H6,pallmall),pet(H6,bird),
%黄色房子主人抽Dunhill 香烟
member(H7,X),color(H7,yellow),smoke(H7,dunhill),
%住在中间房子的人喝牛奶
center(H8,X),drink(H8,milk),
% 挪威人住第一间房
nation(H9,norwegian),first(H9,X),
% 抽Blends香烟的人住在养猫的人隔壁
member(H10a,X),member(H10b,X),smoke(H10a,blend),pet(H10b,cat),neighbor(H10a,H10b,X),
%抽Blue Master的人喝啤酒
member(H11,X),smoke(H11,bluemaster),drink(H11,bier),
%养马的人住抽Dunhill 香烟的人隔壁
member(H12a,X),member(H12b,X),pet(H12a,horse),smoke(H12b,dunhill),neighbor(H12a,H12b,X),
% 德国人抽Prince香烟
member(H13,X),nation(H13,german),smoke(H13,prince),
% 挪威人住蓝色房子隔壁
member(H14a,X),member(H14b,X),nation(H14a,norwegian),color(H14b,blue),neighbor(H14a,H14b,X),
% 抽Blends香烟的人有一个喝水的邻居
member(H15a,X),member(H15b,X),smoke(H15a,blend),drink(H15b,water),neighbor(H15a,H15b,X),
%问题“谁养鱼?”其实也提供了一个信息,表明有一间房子的主人养鱼,把这个信息也要加上
member(H16,X),pet(H16,fish).
好,现在用swi prolog consult这个程序,在:-前输入einsten(X).回车,得到问题的解:
X = [h(norwegian, yellow, water, cat, dunhill), h(dane, blue, tea, horse, blend), h(englishman, red, milk, bird, pallmall), h(german, green, coffee, fish, prince), h(swede, white, bier, dog, bluemaster)] ;
这是满足上面所有描述的唯一正确房子列表。观察这个房子列表,得到问题的答案是:德国人养鱼。
从解决这个问题所犯的错误中我总结了如下经验和教训:
1,一定要把prolog的三种语句:事实、规则、问题,完全搞清楚。
2,要理解prolog求解问题的原理,也就是要把霍恩子句逻辑搞清楚。
3,解决问题的关键在于清楚地描述问题所需要的全部条件,人解决问题的时候难免会使用直觉,而在下意识中使用某些隐含条件,要让prolog正确解决问题,就应该把这些隐含条件也挖掘出来。
prolog是一种基于一阶谓词逻辑的语言,它在人工智能程序设计中有广泛应用。