爱因斯坦的超级问题(人工智能实验二报告)

实验题目

Prolog编程求解图搜索问题

实验目的

  • 熟悉Prolog的运行环境,进行Prolog的基本编程练习

      了解Prolog语言中常量、变量的表示方法。Prolog的简单程序机构,掌握分析问题、询问解释技巧;进行事实库、规则库的编写,并在此基础上进行简单的询问。

  • 求解图搜索问题

      任选一个以下实际应用题目:爱因斯坦的超级问题、字谜问题、汉诺塔问题、八数码问题、八皇后问题、农夫过河问题、传教士与野人问题。

实验平台

  • 硬件:计算机
  • 软件:windows/Linux操作系统
  • 应用软件:Prolog

实验内容

熟悉Prolog语言的使用并实现求解图搜索问题

实验步骤

  • 安装Prolog集成开发环境;
  • 采用Prolog编写所选问题的源程序;
  • 编译程序,输出查询问题的结果或数据。

具体设计

1. 题目详情

  本次实验我选择的是第一个题目——爱因斯坦的超级问题(因为题目太吸引人了)。我在网络上找到了两个版本的题目,虽说内容不太一样,但是框架都是一致的。下面给出我所做的版本的题目:

有五栋五种颜色的房子,每一位房子的主人国籍都不同;每个人只喝一种饮料,只抽一种牌子的香烟,也只养一只宠物。没有人有相同的宠物,抽相同牌子的香烟,喝相同的饮料。已知:

(1)英国人住在红房子里;

(2)瑞典人养了一条狗;

(3)丹麦人喝茶;

(4)绿房子在白房子左边;

(5)绿房子主人喝咖啡;

(6)抽PALL MALL烟的人养了一只鸟;

(7)黄房子主人抽DUNHILL烟;

(8)住在中间房子的人喝牛奶;

(9)挪威人住在第一间房子;

(10)抽混合烟的人住在养猫人的旁边;

(11)养马人住在抽DUNHILL烟的人的旁边;

(12)抽BLUE MASTER烟的人喝啤酒;

(13)德国人抽PRINCE烟;

(14)挪威人住在蓝房子旁边;

(15)抽混合烟的人的邻居喝矿泉水;

问题:谁养鱼?

2. 题目分析

  使用目标导向分析,我们需要写出一个Prolog程序来解答这个问题,那么首先要明确Prolog程序的组成部分。Prolog程序一般由一组事实规则问题组成。所以,我们首先要弄清楚爱因斯坦的超级问题中,事实、规则、问题分别是什么。

  “事实”实际上已经很清楚了,就是题目详情中所给的第一段话以及15个已知信息;问题也很明确:找到养鱼的那个人。但对于此题,将所有人的国籍、养的宠物、房子的颜色、喝的饮料以及抽的烟全部匹配出来同样是可行的。所以问题可以变为,求出使全部事实成立的5个匹配。

  相比较与”事实“和“问题”的清晰明了,“规则”就比较隐秘了,需要我们从题目中挖掘:

  • 通过已知信息(4),我们知道需要定义规则“A在B的左边(相邻)”。一共有五座房子,若从左到右依次标号为1-5,那么“(A,B)分别为(1,2)、(2,3)、(3,4)、(4,5)”都符合“规则A在B的左边(相邻)”。
  • 通过已知信息(8),我们知道需要定义规则“A房子在中间”。若让五座房子沿用上面1-5的编号,则只有“A房子标号为3”才符合规则“A房子在中间”。
  • 通过已知信息(9),我们知道需要定义规则“A房子是第一个房子”。若让五座房子沿用上面1-5的编号,则只有“A房子标号为1”才符合规则“A房子是第一个房子”。
  • 通过已知信息(10)、(11)、(14),我们知道需要定义规则“A在B的旁边”。那么“(A,B)分别为(1,2)、(2,3)、(3,4)、(4,5)、(2,1)、(3,2)、(4,3)、(5,4)”都符合规则“A在B的旁边”。
  • 由于房主人的国籍、房主人所养的宠物、房子的颜色、房主人喝的饮料、房主人抽的烟都是匹配好的,所以我们假设:在使用与这5个属性的其中之一属性相关的任何一个规则时,都将其他4个属性与其绑定处理,看作一个整体,只不过这个整体除了规则涉及到的属性之外,还有其他的属性。
  • 由于所有的属性都与人息息相关,所以我们可以设定谓词people(Country, Pet, Color, Drink, Smoke)。
  • 五个房子从左到右又有五个位置,所以我们可以设定谓词house(A, Houses),其中A就是使事实成立的一个people实例,house(A, Houses)表示A的房子在五个房子中的相对位置,一共有五种。
  • Houses表示五个房子的布局。由于五个房子有一个从左到右的顺序,所以很容易想到将Prolog中的表用在Houses上(相关知识介绍放在最后)。
  • 最后一个规则就是,当所有事实成立时,我们便可得出一个布局。

  根据以上的分析,我们很快就可以写出解此题需要用到的规则。

3. Prolog代码

ESP.pl文件:(ESP即Einstein’s super problem,注意文件名后缀)

/*
 *表[_,_,_,_,_]为Houses
 *表中每个元素都是people
 *这个表即表明这五个人的房子的布局
 */
 
/******规则部分******/

/***规则1:A在B的左边***/
left(A,B,[A,B,_,_,_]).
left(A,B,[_,A,B,_,_]).
left(A,B,[_,_,A,B,_]).
left(A,B,[_,_,_,A,B]).

/***规则2:A在中间***/
middle(A,[_,_,A,_,_]).

/***规则3:A是第一个***/
first(A,[A,_,_,_,_]).

/***规则4:A在B的旁边***/
neighbor(A,B,[A,B,_,_,_]).
neighbor(A,B,[_,A,B,_,_]).
neighbor(A,B,[_,_,A,B,_]).
neighbor(A,B,[_,_,_,A,B]).
neighbor(A,B,[B,A,_,_,_]).
neighbor(A,B,[_,B,A,_,_]).
neighbor(A,B,[_,_,B,A,_]).
neighbor(A,B,[_,_,_,B,A]).

/***规则5:房子A有五个位置可选***/
house(A,[A,_,_,_,_]).
house(A,[_,A,_,_,_]).
house(A,[_,_,A,_,_]).
house(A,[_,_,_,A,_]).
house(A,[_,_,_,_,A]).

/***整体people,属性包括Country,Pet,Color,Drink,Smoke***/
people(Country,Pet,Color,Drink,Smoke).

/******事实、规则******/
all_houses(Houses) :-
    house(people(british,_,red,_,_), Houses),/*事实1*/
    house(people(swedish,dog,_,_,_), Houses),/*事实2*/
	house(people(danish,_,_,tea,_),Houses),/*事实3*/
	left(people(_,_,green,_,_), people(_,_,white,_,_), Houses),/*事实4*/
	house(people(_,_,green,coffee,_),Houses),/*事实5*/
	house(people(_,bird,_,_,pallmall),Houses),/*事实6*/
	house(people(_,_,yellow,_,dunhill),Houses),/*事实7*/
	middle(people(_,_,_,milk,_), Houses),/*事实8*/
    first(people(norway,_,_,_,_), Houses),/*事实9*/
	neighbor(people(_,_,_,_,blend),people(_,cat,_,_,_),Houses),/*事实10*/
	neighbor(people(_,horse,_,_,_),people(_,_,_,_,dunhill),Houses),/*事实11*/
	house(people(_,_,_,beer,bluemaster),Houses),/*事实12*/
	house(people(german,_,_,_,prince),Houses),/*事实13*/
	neighbor(people(norway,_,_,_,_),people(_,_,blue,_,_),Houses),/*事实14*/
	neighbor(people(_,_,_,_,blend),people(_,_,_,mineralwater,_),Houses),/*事实15*/
	
	/*问题是谁养鱼,虽然鱼在上面的事实中都没有出现,但是一定有一个人养鱼*/
	house(people(_,fish,_,_,_),Houses).

4. 运行结果

Prolog表相关知识

  表是Prolog中一种非常有用的数据结构。表的表述能力很强,数字中的序列、集合,通常语言中的数组、记录等均可用表来表示。表的最大特点是其长度不固定,在程序的运行过程中可动态地变化。具体来讲,就是在程序运行时,可对表实行一些操作,如给表中添加一个元素,或从中删除一个元素,或者将两个表合并为一个表等。用表还可以方便地构造堆栈、队列、链表、树等动态数据结构。

  表还有一个重要特点,就是它可以分为头和尾两部分。表头是表中第一个元素,而表尾是表中除第一个元素外的其余元素按原来顺序组成的表。

  在程序中是用“|”来区分表头和表尾的,而且还可以使用变量.例如一般地用"[H|T]"来表示一个表,其中 H、T 都是变量,H 为表头,T为表尾。注意,此处 H 是一个元素(表中第一个元素),而 T 则是一个表(除第一个元素外表中的其余元素按原来顺序组成的表)。表的这种表示法很有用,它为表的操作提供了极大的方便 。

  • 表的匹配合一

  如下面的表所示即为用这种表示法通过匹配合一提取表头和表尾的例子.。

表1表2合一后的变量值
[X | Y][a, b, c]X = a, Y = [b, c]
[X | Y][a]X = a, Y = []
[a | Y][X, b]X = a, Y = [b]
[X, Y, Z][a, b, c]X = a, Y = b, Z = c
[[a, Y] | Z][[X, b], [c]]X = a, Y = b, Z = [[c]]

  还需说明的是,表中的“|”后面只能有一个变量。例如写法 [X | Y, Z] 就是错误的,但竖杠前面的变量可以多于一个:例如写法 [ X, Y | Z] 是允许的。这样,这个表同 [a, b, c] 匹配合一后,有:X = a, Y = b, Z =[c]。

  另外,竖杠的前面和后面也可以是常量,例如 [a | Y] 和 [X | b] 都是允许的,但需要注意,后一个表称为无尾表,如果它同表 [a | Y] 匹配,则有:X = a, Y = b (而不是 Y = [b])

  更多Prolog相关知识

Prolog安装及使用

本次实验我是在Ubuntu16.04下完成的。在终端键入

sudo apt-get install swi-prolog

安装成功后,键入

prolog XXX.pl

swipl XXX.pl

即可执行写好的Prolog程序。

此外,还可以使用命令行交互运行程序:

键入

swipl

prolog

在 “?-” 符号后面敲 [user]. (注意别把 “.” 丢掉,. 在 prolog 程序里面是结束符,类似 C 的 ;)敲两下回车看到 “|:” 符号,即可写入事实与规则。输入完成后,按ctrl+D退出代码编辑,当再次出现“?-”时,就可以针对刚刚输入的内容进行询问了。

讨论与结论

1. 重难点讨论

  本次实验重点在于理解题意,难点在于学习Prolog语言语法以及Prolog的安装与使用,并根据题意写出正确的Prolog程序。理解题意是根本,这个题目本身也比较清晰。Prolog语言语法、安装与使用的方法主要通过网络资料、博客获取。刚开始我没有系统地学习Prolog语言语法就上手写,导致报了一堆错误,后来学习后才知道,原子与函子都要以小写字母为开头。

2. 实验收获

  通过本次实验,我学会了使用SWI-prolog、了解了Prolog语言的一些语法,也锻炼了自己分析题目、编写Prolog程序的能力。但是由于刚刚入门,所以对Prolog各方面知识的运用都还不太熟练,掌握的也不是那么全面,只对自己使用到的部分和一些基础部分有所了解。因此在实验后需要我加以巩固,更多地去运用Prolog工具,提升熟练度,比较系统全面的学习Prolog的语法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值