人工智能导论实验报告
- 熟悉PROLOG的运行环境,进行PROLOG的基本编程练习;
- 了解PROLOG语言中常量、变量的表示方法。PROLOG的简单程序结构,掌握分析问题、询问解释技巧;进行事实库、规则库的编写,并在此基础上进行简单的询问。
- 求解图搜索问题。
实际应用题目:传教士与野人问题
硬件:计算机
软件:操作系统:WINDOWS
应用软件:SWI-Prolog
熟悉prolog语言的使用并实现求解图搜索问题——传教士与野人过河问题;
【问题描述】
有 N 个传教士和 N 个野人来到河边渡河,河岸有一条船,每次至多可供 k 人乘渡。问:传教士为了安全起见,应如何规划摆渡方案,使得任何时刻, 河两岸以及船上的野人数目总是不超过传教士的数目(否则不安全,传教士有可能被野人吃掉)。 即求解传教士和野人从左岸全部摆渡到右岸的过程中,任何时刻满足 M (传教土数) ≥ C (野人数)和 M+C≤k 的摆渡方案。
【问题分析】
假设以传教士和野人的数量N为3,船一次最大的载人量K为2进行分析。
初始状态:河左岸有3个野人河3个传教士;河右岸有0个野人和0个传教士;船停在左岸,船上有0个人。
目标状态:河左岸有0个野人和0个传教士;河右岸有3个野人和3个传教士;船停在右岸,船上有0个人。
2.1. 安装prolog集成开发环境
此时可以进行源程序的编写,通过【File】-【new …】新建pl文件,编写源程序;
2.2. 采用prolog编写所选问题的源程序
对该问题的程序设计分为五个部分:
-
设计问题的状态
首先需要使用Prolog的数据结构来表达两岸的状态(传教士、野人数量、小船所在位置);
用如下复合结构来表达问题的某个状态:
((左岸传教士数,左岸野人数),(右岸传教士数,右岸野人数),船的位置)
船的位置为0表示船在左岸,为1表示在右岸,一开始,所有的人与船都在右岸;
因此,初始状态为:((0,0),(3,3),1),目标状态为:((3,3),(0,0),0);
当然,实际上是没有必要包括两个岸上的人数的,因为一个岸上的人数可以通过总人数减去另一岸上的人数来算出,不过这里写出两个岸上的人数便于理解。 -
描述可能的动作
船上所载人的方案就是可能的动作,这里用谓词move表示;
move(x,y),表示船上载x个传教士、y野人,x+y<2;
根据要求,共得出以下5中可能的动作:①. 渡2传教士 —— move(2,0)
②. 渡2野人 —— move(0,2)
③. 渡1野人1传教士 —— move(1,1)
④. 渡1传教士 —— move(1,0)
⑤. 渡1野人 —— move(0,1)
第一次接触prolog语言,不得不说这个语言十分有趣,能通过它进行一些推理,甚至可以实现递归、迭代、搜索算法等等操作。
在Prolog的程序的运行流程方面我有了如下的认识:规则的运行是通过Prolog内建的回溯功能实现的、我们可以使用内部谓词fail来强制实现回溯、我们也可以通过加入一条参数为伪变量(下划线)无Body部分的子句,来实现强制让谓词成功。数据库中的事实代替了一般语言中的数据结构。回溯功能能够完成一般语言中的循环操作。而通过模式匹配能够完成一般语言中的判断操作。规则能够被单独地调试,它和一般语言中的模块相对应。而规则之间的调用和一般语言中的函数的调用类似。
当然,这次实验也稍有一些不足,传教士与野人问题是可以进行一个扩展到一般性情况的问题,这里因时间原因只写出了它的传教士、野人数目确认的情况,后面尝试去求解它的一般性情况。
这次实验非常的有趣,受益匪浅。
move(1,0).
move(0,1).
move(0,2).
move(2,0).
move(1,1).
legal((X,Y,_)):-
legal_an(X),
legal_an(Y).
legal_an((A,B)):- (A=:=0,B>=0,!);(B=:=0,A>=0,!);(A>=B,A>=0,B>=0).
update((X,Y,Q),Move,Statu):-(A,B)=X, (C,D)=Y,(E,F)=Move,
if_then_else(
Q=:=0, %Q == 0
(C1 is C+E, D1 is D+F, A1 is A-E, B1 is B-F, Statu=((A1,B1),(C1,D1),1)),
(C1 is C-E, D1 is D-F, A1 is A+E, B1 is B+F, Statu=((A1,B1),(C1,D1),0))
).
nextstatu(Statu,Statu1):- %
move(X,Y),
update(Statu,(X,Y),Statu1),
legal(Statu1).
if_then_else(P,Q,R):- call§,!,Q.
if_then_else(P,Q,R):- R.
nx(X,Y):- reverse(X,Y).
first(A,X):- append([A],_,X). %AX
last(B,X):- first(A,X),append([A],B,X). %
show(L):-if_then_else((length(L,X),X>0), %LX>0
(first(A,L),last(B,L),write(’[’),write(A),write(’]’),nl,show(B)),
fail). %
findroad(X,Y,L):-
if_then_else(X=Y,
(write(’------------’),nl,nx(L,LN),show(LN),nl), %nxL
(nextstatu(X,Z),not(member(Z,L)),findroad(Z,Y,[Z|L]))). %ZL
?- findroad(((0,0),(3,3),1),((3,3),(0,0),0),[((0,0),(3,3),1)]).
[(0,0),(3,3),1]
[(0,2),(3,1),0]
[(0,1),(3,2),1]
[(0,3),(3,0),0]
[(0,2),(3,1),1]
[(2,2),(1,1),0]
[(1,1),(2,2),1]
[(3,1),(0,2),0]
[(3,0),(0,3),1]
[(3,2),(0,1),0]
[(2,2),(1,1),1]
[(3,3),(0,0),0]
[(0,0),(3,3),1]
[(0,2),(3,1),0]
[(0,1),(3,2),1]
[(0,3),(3,0),0]
[(0,2),(3,1),1]
[(2,2),(1,1),0]
[(1,1),(2,2),1]
[(3,1),(0,2),0]
[(3,0),(0,3),1]
[(3,2),(0,1),0]
[(3,1),(0,2),1]
[(3,3),(0,0),0]