失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 人工智能导论实验2——农夫过河问题

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

时间:2020-10-08 06:34:37

相关推荐

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

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

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

一、实验目的

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

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

求解图搜索问题。

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

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

硬件:计算机

软件:操作系统: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],[]).

如果觉得《人工智能导论实验2——农夫过河问题》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。