人工智能之传教士和野人过河问题

Posted 爱上游戏开发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了人工智能之传教士和野人过河问题相关的知识,希望对你有一定的参考价值。

推荐阅读:

设有3个传教士和3个野人来到河边,打算乘一条船从右岸渡到左岸去。该船的负载能力为2人。在任何时候,如果野人人数超过传教士人数,那么野人就会把传教士吃掉。怎样使用这条船安全地摆渡所有人。

对于这个问题我们先定义核心规则:
(1)核心规则是river(integer,integer,integer,interger,integer, integerlist):-…river(…).

规则表示将第一个river状态转移为第二个river状态。river的第一、二个参数分别是右岸的传教士数量、野人数量;第三、四个参数是左岸传教士数量、野人数量;第五个参数为0表示船在右岸,为1表示船在左岸;最后一个参数是列表,用来记录状态,状态由river的前5个参数组成,新的状态添加进列表中。

举例说明,3个传教士,1个野人过河,goal为:
river(3,1,0,0,0 ,[3,1,0,0,0]).
参数的含义是:右岸3个传教士,1个野人,左岸0个传教士,0个野人,船在右岸,初始状态为[3,1,0,0,0]。
状态序列为:
1)3 1 0 0 0
2)2 0 1 1 1
3)2 1 1 0 0
4)0 1 3 0 1
5)1 1 2 0 0
6)1 0 2 1 1
7)2 0 1 1 0
8)0 0 3 1 1
从1)到2),表示有1个传教士和1个野人到从右岸到左岸,2)中船已经在左岸。从2)到3),表示有1个传教士从左岸到右岸,3)中船已经在右岸。2)7)虽然左岸右岸人的分布一样,但船的位置不同,所以是不同状态。

(2)可以写多条river规则,如从右岸到左岸,一次可以2人传教士,或2个野人,或1传教士1野人,或其他。可以写多条规则分别对这些情况进行处理。

(3)在规则中要有条件判断,如从右到左摆渡2传教士,条件检查是:右岸应该有2传教士,且右岸少掉2传教士后右岸的野人不能吃传教士,原因或者右岸的传教士不比野人少,或者右岸根本没有传教士了。

(4)river的前5个参数其实表达状态,river规则中右边的river是转移的目标状态,对于目标状态要检查其是否在状态列表中。状态检查其实是检查一个5元素组成的状态表是否在状态列表中。若在,表示状态转移在循环,应该fail,取消到目标状态的转移;若不在,将目标状态添加进状态列表中。若已到达最终目标状态,则输出状态列表,如前例,一行显示一个状态。前例中的目标状态是
0 0 3 1 1,代码退出规则可以写为:
river(0,0,L3,L4,1,SL):-
writeSL(SL).
在前例中,结束时SL为:
3 1 0 0 02 0 1 1 12 1 1 0 00 1 3 0 11 1 2 0 01 0 2 1 12 0 1 1 00 0 3 1 1
是将1)至8)的状态写在一个表中,每5个数据表示一个状态。

(5)要对初始化状态错误的检查,初始右岸的传教士少于野人,则失败。对应规则可以写为:
river(L1,L2,0,0,0,SL):- L1<>0,L1<L2,!,fail.
Goal中的river会匹配这条规则,若规则右边2个条件成立,则执行流程会从左到右穿过!,到达fail,表示规则右边失败,又执行流程不能从右到左通过!,即左侧river不能回溯,于是返回结论,river失败。

(6)思考,初始状态下,当传教士和野人数量相等,且都大于3人时,是否有解(以3传教士,3野人为基础进行思考)。将这种情况写进规则中。

通过上面的分析,我们最终得出如下代码:

DOMAINS
 integerlist = integer*

PREDICATES

nondeterm river(integer,integer,integer,integer, integer, integerlist)
writelist(integerlist)
nondeterm move(integer,integer)
comparenum(integer,integer)
 compareSL(integerlist,integerlist)

clauses
writelist([]).
writelist([X1,X2,X3,X4,X5|Tail]):-
write(X1),write(""),write(X2),write(""),write(X3),write(""),write(X4),write(""),write(X5),write(""),nl,
writelist(Tail).

compareSL(_,[]).
   compareSL([X1,X2,_,_,X3],[Y1,Y2,_,_,Y3|_]):-X1=Y1,X2=Y2,X3=Y3,!,fail.
   compareSL(L,[_,_,_,_,_|SL]):- compareSL(L,SL).   


move(2,0).
move(1,0).
move(0,2).
move(0,1).
move(1,1).

comparenum(0,_):-!.
comparenum(L1,L2):-
L1>=L2.


river(0,0,_,_,1,StateList):-
write("finish"),nl,
writelist(StateList).


river(L1,L2,L3,L4,0,StateList):-
move(N1,N2),
L11=L1-N1,L11>=0,
L33=L3+N1,
L22=L2-N2,L22>=0,
L44=L4+N2,
comparenum(L11,L22),
comparenum(L33,L44),
compareSL([L11,L22,L33,L44,1],StateList),
StateList2 = [L11,L22,L33,L44,1|StateList],
river(L11,L22,L33,L44,1,StateList2).


river(L1,L2,L3,L4,1,StateList):-
move(N1,N2),
L11=L1+N1,
L33=L3-N1,L33>=0,
L22=L2+N2,
L44=L4-N2,L44>=0,
comparenum(L11,L22),
comparenum(L33,L44),
compareSL([L11,L22,L33,L44,0],StateList),
StateList2 = [L11,L22,L33,L44,0|StateList],
river(L11,L22,L33,L44,0,StateList2).


goal
ERE="CHUANJIAOSHI YU YEREN",
river(3,3,0,0,0,[3,3,0,0,0]).

实验结果:
finish
0 0 3 3 1
1 1 2 2 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
ERE=CHUANJIAOSHI YU YEREN
finish
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
ERE=CHUANJIAOSHI YU YEREN
finish
0 0 3 3 1
1 1 2 2 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
ERE=CHUANJIAOSHI YU YEREN
finish
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
ERE=CHUANJIAOSHI YU YEREN
4 Solutions

以上是关于人工智能之传教士和野人过河问题的主要内容,如果未能解决你的问题,请参考以下文章

过河问题(牛虎过河商人仆人过河农夫妖怪过河传教士野人过河)(第2届第2题)

三人三鬼过河(野人与传教士过河)问题c/c++代码

AI传教士和野人渡河问题-实验报告

修道士和野人问题

2021-05-18 C#.NET面试题 三名传教士和三个野蛮人同在一个小河渡口,渡口上只有一条可容两人的小船。问题的目标是要用这条小船把这六个人全部渡到对岸去,条件是在渡河的过程中,河两岸随时都保持

[NOI2002] 荒岛野人 扩展欧几里得算法