这次人工智能实验课,老师要求我们通过代码解决谓词逻辑表示中的一个经典问题——猴子香蕉问题,在上理论课我们自己推导得时候感觉不太难,但是对于如何用代码实现,一下子还是比较懵的。
具体问题描述是这样的:一个房间里,天花板上挂有一串香蕉,有一只猴子可在房间里任意活动(到处走动,推移箱子,攀登箱子等),房间里还有一只可被猴子移动的箱子,且猴子登上箱子时才能摘到香蕉,问猴子在某一状态下(设猴子位置为A,箱子位置为B,香蕉位置在C),如何行动可摘取到香蕉。
1 定义描述状态的谓词
对该问题进行形式化描述,我们会得到整个空间的两个状态:
-
初始状态:猴子处在 A 处,箱子处在 B 处,香蕉悬挂在 C 处
-
目标状态:猴子和箱子同处 C 处,且猴子站在箱子 B 上摘到香蕉
对上述状态我们进行谓词的定义:
- M o n k e y ( x ) \bf{Monkey(x)} Monkey(x):猴子 monkey 位于 x 处
- B o x ( x ) \bf{Box(x)} Box(x):箱子 box 位于 x 处
- B a n a n a ( x ) \bf{Banana(x)} Banana(x):香蕉 banana 位于 x 处
- O n ( y , z ) \bf{On(y, z)} On(y,z):y 在 z 上
- H o l d ( y , w ) \bf{Hold(y, w)} Hold(y,w):y 拿到 w
其中,变元 x , y , z , w x, y, z, w x,y,z,w 的个体域分别为:
- x x x:个体域是 { A , B , C } \{A, B, C\} { A,B,C}
- y y y:个体域是 { m o n k e y } \{monkey\} { monkey}
- z z z:个体域是 { b o x } \{box\} { box}
- w w w:个体域是 { b a n a n a } \{banana\} { banana}
因此我们整个空间的初始和目标状态就可以描述为:
-
初始状态: M o n k e y ( A ) ∧ B o x ( B ) ∧ B a n a n a ( C ) ∧ ¬ O n ( m o n k e y , b o x ) ∧ ¬ H o l d ( m o n k e y , b a n a n a ) \bf{Monkey(A) \land Box(B) \land Banana(C) \land \lnot On(monkey, box) \land \lnot Hold(monkey, banana)} Monkey(A)∧Box(B)∧Banana(C)∧¬On(monkey,box)∧¬Hold(monkey,banana)
-
目标状态: M o n k e y ( C ) ∧ B o x ( C ) ∧ B a n a n a ( C ) ∧ O n ( m o n k e y , b o x ) ∧ H o l d ( m o n k e y , b a n a n a ) \bf{Monkey(C) \land Box(C) \land Banana(C) \land On(monkey, box) \land Hold(monkey, banana)} Monkey(C)∧Box(C)∧Banana(C)∧On(monkey,box)∧Hold(monkey,banana)
我们已经知道怎么用谓词对上述状态进行定义了,那么我们怎么使用代码来描述它们呢?为了使问题更具一般化,我们将 A、B、C 三点抽象化,分别用 -1、0、1 来表示,并且对于猴子 monkey 是否在箱子 box 上,以及猴子 monkey 是否拿到香蕉 banana,我们用 -1 表示否,用 1 表示是,这样我们就可以定义一个数据结构,我们写成一个类 State:
class State:
'''
msg: 对状态和动作进行封装
param {
monkey: 猴子 monkey 位于何处
box: 箱子 box 位于何处
banana: 香蕉 banana 位于何处
monbox: 猴子 monkey 是否在箱子 box 上
mholdban: 猴子 monkey 是否拿到香蕉 banana
}
'''
def __init__(self, monkey=-1, box=0, banana=1, monbox=-1, mholdban=-1):
self.monkey = monkey # -1: Monkey at A, 0: Monkey at B, 1: Monkey at C
self.box = box # -1: box at A, 0: box at B, 1: box at C
self.banana = banana # -1: Banana at A, 0: Banana at B, 1: Banana at C
self.monbox = monbox # -1: monkey not on the box, 1: monkey on the box
self.mholdban = mholdban # -1: monkey not hold banana, 1: monkey hold banana
我们再写一个映射的函数,方便之后把 -1、0、1 映射回 A、B、C:
def transform(x):
'''
msg: 将 A、B、C 三点抽象化,分别用 -1、0、1 来表示
'''
if x == -1:
return "A"
elif x == 0:
return "B"
elif x == 1:
return "C"
2 定义操作
我们的目标是把问题的初始状态转换为目标状态,为此需要完成一系列的操作。每个操作一般可分为条件和动作两部分,条件部分用来说明执行该操作必须具备的先决条件,动作部分给出了该操作对问题状态的改变情况。条件部分可用谓词公式来表示,动作部分则是通过在执行该操作前的问题状态中删去和增加相应的谓词来实现的。在本问题中,我们需要执行以下 5 个操作:
- m o n k e y g o t o ( u , v ) \bf{monkeygoto(u, v)} monkeygoto(u,v):猴子 monkey 从 u 走到 v 处
- 条件: ¬ O n ( m o n k e y , b o x ) , M o n k e y ( u ) \lnot On(monkey, box),~Monkey(u) ¬On(monkey,box), Monkey(u)
- 动作:
- 删除表: M o n k e y ( u ) Monkey(u) Monkey(u)
- 添加表: M o n k e y ( v ) Monkey(v) Monkey(v)
- m o v e b o x ( u , v ) \bf{movebox(u, v)} movebox(u,v):猴子 monkey 把箱子 box 从 u 推到 v 处
- 条件: