人工智能导论实验2——农夫过河问题

实验二:Prolog编程求解图搜索问题

(这次实验是看了大佬的传教士与野人问题后自己编写的,大佬太强了)

一、实验目的

  1. 熟悉PROLOG的运行环境,进行PROLOG的基本编程练习;
    

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

  1. 求解图搜索问题。
    

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

二、实验的硬件、软件平台

硬件:计算机

软件:操作系统:WINDOWS/Linux

应用软件:Prolog

SWI-Prolog 官网有各个操作系统的二进制安装包,下载即可

三、实验内容及步骤

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

实验步骤:

1:安装prolog集成开发环境

2:采用prolog编写所选问题的源程序

3:编译程序,输出查询问题的结果或数据

四、分析设计

0、prolog语言基本介绍

2.1 常量和变量Prolog 的变量和常量规则很简单:小写字母开头的字符串,就是常量;大写字母开头的字符串,就是变量。
?- write(abc).
abc
true.

?- write(Abc).
_3386
true.
上面代码中,abc是常量,输出就是自身;Abc是变量,输出就是该变量的值。2.2 关系和属性两个对象之间的关系,使用括号表示。比如,jack 的朋友是 peter,写成friend(jack, peter).。注意,jack 的朋友是 peter,不等于 peter 的朋友是 jack。如果两个人都认为对方是朋友,要写成下面这样。
friend(jack, peter).
friend(peter, jack).
如果括号里面只有一个参数,就表示对象拥有该属性,比如 jack 是男性,写成male(jack).。2.3 规则规则是推理方法,即如何从一个论断得到另一个论断。举例来说,我们定下一条规则:所有朋友关系都是相互的,规则写成下面这样。
friend(X, Y) :- friend(Y,X).
上面代码中,X和Y都是大写,表示这是两个变量。符号:-表示推理关系,含义是只要右边的表达式friend(Y, X)为true,那么左边的表达式friend(X, Y)也为true。因此,根据这条规则,friend(jack, peter)就可以推理得到friend(peter, jack)。如果一条规则取决于多个条件同时为true,则条件之间使用逗号分隔。
mother(X, Y) :- child(Y,X), female(X).
上面代码中,X是Y的母亲(mother(X, Y))取决于两个条件:Y是X的小孩,X必须是女性。只有这两个条件都为true,mother(X, Y)才为true。如果一条规则取决于某个条件为false,则在条件之前加上+表示否定。
onesidelove(X, Y) :- loves(X, Y), + loves(Y,X).
上面代码中,X单相思Y,取决于两个条件。第一个条件是X喜欢Y,第二个条件是Y不喜欢X。2.5 查询Prolog 支持查询已经设定的条件。我们先写一个脚本hello.pl。
friend(john, julia).
friend(john, jack).
friend(julia, sam).
friend(julia, molly).
然后在 SWI-Prolog 里面加载这个脚本。
?- [hello].
true.
上面代码中,true.是返回的结果,表示加载成功。然后,可以查询两个人是否为朋友。
?- friend(john, jack).
true.

?- friend(john, sam).
false.
listing()函数可以列出所有的朋友关系。
?- listing(friend).
friend(john, julia).
friend(john, jack).
friend(julia, sam).
friend(julia, molly).

true.
还可以查询john有多少个朋友。
?- friend(john, Who).
Who = julia ;
Who = jack.
上面代码中,Who是变量名。任意的变量名都可以,只要首字母为大写。

1、题目详情

我选择农夫过河问题,具体问题如下

一个农夫带着一匹狼、一只羊、一颗白菜要过河,只有一条船而且农夫每次最多只能带一个动物或物品过河,并且当农夫不在的时候狼会吃羊,羊会吃白菜,列出所有安全将所有动物和物品带过河的方案。

2、问题分析与程序设计

需要写出一个Prolog程序来解答这个问题,Prolog程序一般由一组事实、规则和问题组成。

事实:一个农夫带着一匹狼、一只羊、一颗白菜要过河,只有一条船。

规则:农夫每次最多只能带一个动物或物品过河。且当农夫不在的时候狼会吃羊,羊会吃白菜。

问题:列出所有安全将所有动物和物品带过河的方案。简化问题狼,羊,白菜过河顺序。

状态空间

(FL,WL,SL,CL,B, FR,WR,SR,CR)分别为左边的即未过河的农夫,狼,羊,白菜以及对应过河的。B的值可为left,right,表示船在左边还是由边。

start([1,1,1,1,left, 0,0,0,0]), 起始状态

goal([0,0,0,0,right,1,1,1,1]), 目标状态。

合法状态定义

当农民不在时即F==0,狼,羊不能同在即W,S都为1,或者羊,白菜同在。

行动
农夫独自过河,带狼或者羊或者白菜过河,分别有从左到右,从右到左。
Action:L(1,0,0,0),L(1,1,0,0),L(1,0,1,0),L(1,0,0,1)
R(1,0,0,0),R(1,1,0,0),R(1,0,1,0),R(1,0,0,1).
状态转移模型
当前状态+Action构成转移模型

3、代码实现注释

%定义状态

start([1,1,1,1,left, 0,0,0,0]), 起始状态

goal([0,0,0,0,right,1,1,1,1]), 目标状态。

%判断状态合法性

legal0(F,W,S,C)
:-
(F >=0, 1>=F),
(W>=0, 1>=W),
(S>=0,1>=S),
(C>=0,1>=C),
not(F=:=0,C=:=1,S=:=1),
not(F=:=0,W=:=1,S=:=1).
legal(FL,WL,SL,CL,FR,WR,SR,CR):-
legal0(FL,WL,SL,CL),
legal0(FR,WR,SR,CR).

%根据action实现转移模型

%一个农夫过河

cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL,SL,CL,right,FR2,WR,SR,CR]):-
FL2 is FL - 1,
FR2 is FR + 1,
legal(FL2,WL,SL,CL,FR2,WR,SR,CR).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL,SL,CL,left,FR2,WR,SR,CR]):-
FL2 is FL +1,
FR2 is FR - 1,
legal(FL2,WL,SL,CL,FR2,WR,SR,CR).

%农夫带狼过河
cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL2,SL,CL,right,FR2,WR2,SR,CR]):-
FL2 is FL - 1,
FR2 is FR + 1,
WL2 is WL - 1,
WR2 is WR + 1,
legal(FL2,WL2,SL,CL,FR2,WR2,SR,CR).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL2,SL,CL,left,FR2,WR2,SR,CR]):-
FL2 is FL +1,
FR2 is FR - 1,
WL2 is WL + 1,
WR2 is WR - 1,
legal(FL2,WL2,SL,CL,FR2,WR2,SR,CR).

%农夫带羊过河
cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL,SL2,CL,right,FR2,WR,SR2,CR]):-
FL2 is FL - 1,
FR2 is FR + 1,
SL2 is SL - 1,
SR2 is SR + 1,
legal(FL2,WL,SL2,CL,FR2,WR,SR2,CR).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL,SL2,CL,left,FR2,WR,SR2,CR]):-
FL2 is FL +1,
FR2 is FR - 1,
SL2 is SL + 1,
SR2 is SR - 1,
legal(FL2,WL,SL2,CL,FR2,WR,SR2,CR).

%农夫带白菜过河
cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL,SL,CL2,right,FR2,WR,SR,CR2]):-
FL2 is FL - 1,
FR2 is FR + 1,
CL2 is CL - 1,
CR2 is CR + 1,
legal(FL2,WL,SL,CL2,FR2,WR,SR,CR2).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL,SL,CL2,left,FR2,WR,SR,CR2]):-
FL2 is FL +1,
FR2 is FR - 1,
CL2 is CL + 1,
CR2 is CR - 1,
legal(FL2,WL,SL,CL2,FR2,WR,SR,CR2).

搜索状态空间
%search(state1,Visited,Path)state1为当前状态,Visited标记已访问状态,用于剪枝,Path为搜索路劲。
search([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],Visited,Path):-
%没有到达目标状态
not([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1]= [0,0,0,0,right,1,1,1,1]),
cross([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],[FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2]),
%如果下一状态state2已经访问,进行剪枝。
not(member([FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2],Visited)),
%递归搜索state2, [state2|Visited], [[state1,state2]|Path]
search([FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2],[[FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2]|Visited],[[[FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],[FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2]]|Path]).

%如果到达目标节点,输出路径。
search([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],_,Path):-
[FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1] =[0,0,0,0,right,1,1,1,1],
print(Path).

%回溯输出路径
print0([A|B]):-
write(A),
write(’->’),
writeln(B).
print([A|List]):-
length(List, L),
not(L = 1),
print(List),
print0(A).
print([A|List]):-
length(List, L),
L = 1,
[B|_] = List,
print0(B),
print0(A).
五、运行结果
运行结果

有两种方案。根据状态变化说明答案

第一种是:

1、农夫带羊从左岸到右岸,然后农夫独自返回左岸;

2、农夫带狼从左岸到右岸,然后农夫带羊返回左岸;

3、农夫带白菜从左岸到右岸,然后农夫独自返回左岸;

4、最后农夫带羊从左岸到右岸,过河成功。

第二种方案:

1、农夫带羊从左岸到右岸,然后农夫独自返回左岸;

2、农夫带白菜从左岸到右岸,然后农夫带羊返回左岸;

3、农夫带狼从左岸到右岸,然后农夫独自返回左岸;

4、最后农夫带羊从左岸到右岸,过河成功。

六、讨论

1、实验收获

新接触了一门语言prolog,这种独立自主去学习新知识的过程才是最大的收获,是对学习能力最大的提升。并且实际设计状态空间,动作,状态转移模型来解决图搜索问题,对搜索算法有了更深入的体会。

2、重点难点讨论。

重点:学会prolog一些基本的方法,并能够运用prolog语言来设计状态空间,动作,状态转移模型来编程解决图搜索问题。

难点:因为prolog比较老旧,网上知识很少,并且集成开发环境显示信息不多,报错不明白,这和第一次接触prolog也有关系,要运用它实际编程难度不小。

七、源码

因为我在windows上的swi-prolog对中文不友好,所以源码中没包含注释,在上文注释部分中写了,

​start([1,1,1,1,left, 0,0,0,0]).
goal([0,0,0,0,right,1,1,1,1]).

illegal(F,W,S,C):-
(F=0,C=1,S=1);
(F=0,W=1,S=1).
legal0(F,W,S,C) :-
(F >=0, 1>=F),
(W>=0, 1>=W),
(S>=0,1>=S),
(C>=0,1>=C),
not(illegal(F,W,S,C)).
legal(FL,WL,SL,CL,FR,WR,SR,CR):-
legal0(FL,WL,SL,CL),
legal0(FR,WR,SR,CR).

cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL,SL,CL,right,FR2,WR,SR,CR]):-
FL2 is FL - 1,
FR2 is FR + 1,
legal(FL2,WL,SL,CL,FR2,WR,SR,CR).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL,SL,CL,left,FR2,WR,SR,CR]):-
FL2 is FL +1,
FR2 is FR - 1,
legal(FL2,WL,SL,CL,FR2,WR,SR,CR).

cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL2,SL,CL,right,FR2,WR2,SR,CR]):-
FL2 is FL - 1,
FR2 is FR + 1,
WL2 is WL - 1,
WR2 is WR + 1,
legal(FL2,WL2,SL,CL,FR2,WR2,SR,CR).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL2,SL,CL,left,FR2,WR2,SR,CR]):-
FL2 is FL +1,
FR2 is FR - 1,
WL2 is WL + 1,
WR2 is WR - 1,
legal(FL2,WL2,SL,CL,FR2,WR2,SR,CR).

cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL,SL2,CL,right,FR2,WR,SR2,CR]):-
FL2 is FL - 1,
FR2 is FR + 1,
SL2 is SL - 1,
SR2 is SR + 1,
legal(FL2,WL,SL2,CL,FR2,WR,SR2,CR).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL,SL2,CL,left,FR2,WR,SR2,CR]):-
FL2 is FL +1,
FR2 is FR - 1,
SL2 is SL + 1,
SR2 is SR - 1,
legal(FL2,WL,SL2,CL,FR2,WR,SR2,CR).

cross([FL,WL,SL,CL,left,FR,WR,SR,CR],[FL2,WL,SL,CL2,right,FR2,WR,SR,CR2]):-
FL2 is FL - 1,
FR2 is FR + 1,
CL2 is CL - 1,
CR2 is CR + 1,
legal(FL2,WL,SL,CL2,FR2,WR,SR,CR2).
cross([FL,WL,SL,CL,right,FR,WR,SR,CR],[FL2,WL,SL,CL2,left,FR2,WR,SR,CR2]):-
FL2 is FL +1,
FR2 is FR - 1,
CL2 is CL + 1,
CR2 is CR - 1,
legal(FL2,WL,SL,CL2,FR2,WR,SR,CR2).
%62
print0([A|B]):-
write(A),
write(’->’),
writeln(B).
print([A|List]):-
length(List, L),
not(L = 1),
print(List),
print0(A).
print([A|List]):-
length(List, L),
L = 1,
[B|_] = List,
print0(B),
print0(A).

search([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],Visited,Path):-
not([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1] = [0,0,0,0,right,1,1,1,1]),
cross([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],[FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2]),
not(member([FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2],Visited)),
search([FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2],[[FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2]|Visited],[[[FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],[FL2,WL2,SL2,CL2,B2,FR2,WR2,SR2,CR2]]|Path]).

search([FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1],_,Path):-
[FL1,WL1,SL1,CL1,B1,FR1,WR1,SR1,CR1] = [0,0,0,0,right,1,1,1,1],
print(Path).

find:-
search([1,1,1,1,left,0,0,0,0],[1,1,1,1,left,0,0,0,0],[]).

  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值