自己动手写确定性知识系统(一)——初步搭建谓词逻辑表示框架

这次人工智能实验课,老师要求我们通过代码解决谓词逻辑表示中的一个经典问题——猴子香蕉问题,在上理论课我们自己推导得时候感觉不太难,但是对于如何用代码实现,一下子还是比较懵的。

具体问题描述是这样的:一个房间里,天花板上挂有一串香蕉,有一只猴子可在房间里任意活动(到处走动,推移箱子,攀登箱子等),房间里还有一只可被猴子移动的箱子,且猴子登上箱子时才能摘到香蕉,问猴子在某一状态下(设猴子位置为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 处
    • 条件: ¬ O n ( m o n k e y , b o x ) ,   M o n k e y ( u ) ,   B o x ( u ) \lnot On(monkey, box),~Monkey(u),~Box(u) ¬On(monkey,box), Monkey(u), Box(u)
    • 动作:
      • 删除表: M o n k e y ( u ) ,   B o x ( u ) Monkey(u),~Box(u) Monkey(u), Box(u)
      • 添加表: M o n k e y ( v ) ,   B o x ( v ) Monkey(v),~Box(v) Monkey(v), Box(v)
  • c l i m b o n t o \bf{climbonto} climbonto:猴子 monkey 爬上箱子 box
    • 条件: ¬ O n ( m o n k e y , b o x ) ,   M o n k e y ( u ) ,   B o x ( u ) \lnot On(monkey, box),~Monkey(u),~Box(u) ¬On(monkey,box), Monkey(u), Box(u)
    • 动作:
      • 删除表: ¬ O n ( m o n k e y , b o x ) \lnot On(monkey, box) ¬On(monkey,box)
      • 添加表: O n ( m o n k e y , b o x ) On(monkey, box) On(monkey,box)
  • c l i m b d o w n \bf{climbdown} climbdown:猴子 monkey 爬下箱子 box
    • 条件: O n ( m o n k e y , b o x ) ,   M o n k e y ( u ) ,   B o x ( u ) On(monkey, box),~Monkey(u),~Box(u) On(monkey,box), Monkey(u), Box(u)
    • 动作:
      • 删除表: O n ( m o n k e y , b o x ) On(monkey, box) On(monkey,box)
      • 添加表: ¬ O n ( m o n k e y , b o x ) \lnot On(monkey, box) ¬On(monkey,box)
  • h o l d \bf{hold} hold:猴子 monkey 拿到香蕉 banana
    • 条件: O n ( m o n k e y , b o x ) ,   M o n k e y ( u ) ,   B o x ( u ) ,   B a n a n a ( u ) ,   ¬ H o l d ( m o n k e y , b a n a n a ) On(monkey, box),~Monkey(u),~Box(u),~Banana(u),~\lnot Hold(monkey,banana) On(monkey,box), Monkey(u), Box(u), Banana(u), ¬Hold(monkey,banana)
    • 动作:
      • 删除表:   ¬ H o l d ( m o n k e y , b a n a n a ) ~\lnot Hold(monkey,banana)  ¬Hold(monkey,banana)
      • 添加表: H o l d ( m o n k e y , b a n a n a ) Hold(monkey,banana) Hold(monkey,banana)

在代码中,我们定义不同的函数来描述它们:

  • m o n k e y g o t o ( u , v ) \bf{monkeygoto(u, v)} monkeygoto(u,v):猴子 monkey 从 u 走到 v 处
    def monkeygoto(source, target):
        print(f"Monkey go to {transform(target)} from {transform(source)}")
    
  • m o v e b o x ( u , v ) \bf{movebox(u, v)} movebox(u,v):猴子 monkey 把箱子 box 从 u 推到 v 处
    def movebox(source, target):
        print(f"Monkey move box to {transform(target)} from {transform(source)}")
    
  • c l i m b o n t o \bf{climbonto} climbonto:猴子 monkey 爬上箱子 box
    def climbonto():
        print("Monkey climb onto the box")
    
  • c l i m b d o w n \bf{climbdown} climbdown:猴子 monkey 爬下箱子 box
    def climbdown():
        print("Monkey climb down from the box")
    
  • h o l d \bf{hold} hold:猴子 monkey 拿到香蕉 banana
    def hold():
        print("Monkey hold the banana")
    

3 对状态和动作进行封装

在对状态和操作进行定义和描述后,我们应当在代码中以类的形式对它们进行集成和封装,用这个类来对整个空间的状态进行抽象,可以帮助我们在后面的递归推理中只关注每一个中间状态之间的关系,而无需考虑每一个状态究竟是怎样的:

class State:
    '''
    msg: 对状态和动作进行封装
    param {
        monkey: int, 表示猴子 monkey 位于何处
        box: int, 表示箱子 box 位于何处
        banana: int, 表示香蕉 banana 位于何处
        monbox: int, 表示猴子 monkey 是否在箱子 box 上
        mholdban: int, 表示猴子 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

    def monkeygoto(self, target, routesave, i):
        '''
        msg: 猴子 monkey 从 self.monkey 处走到 target 处
        param {
            target: int, 表示移动的目标
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, f"Monkey go to {transform(target)} from {transform(self.monkey)}")
        self.monkey = target

    def movebox(self, target, routesave, i):
        '''
        msg: 猴子 monkey 把箱子 box 从 self.monkey 处推到 target 处
        param {
            target: int, 表示移动的目标
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, f"Monkey move box to {transform(target)} from {transform(self.monkey)}")
        self.monkey = target
        self.box = target

    def climbonto(self, routesave, i):
        '''
        msg: 猴子 monkey 爬上箱子 box
        param {
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, "Monkey climb onto the box")
        self.monbox = 1

    def climbdown(self, routesave, i):
        '''
        msg: 猴子 monkey 爬下箱子 box
        param {
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, "Monkey climb down from the box")
        self.monbox = -1

    def hold(self, routesave, i):
        '''
        msg: 猴子 monkey 拿到香蕉 banana
        param {
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, "Monkey hold the banana")
        self.mholdban = 1

4 递归推理

在做好所有准备工作后,我们就要开始推理了,在本问题中,整个推理过程如下:

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 )                                                         ⇓ m o n k e y g o t o ( A , B ) M o n k e y ( B ) ∧ 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 )                                                         ⇓ m o v e b o x ( B , C ) 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 )                                                         ⇓ c l i m b o n t o 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 )                                                         ⇓ h o l d 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 ) \begin{aligned} &{Monkey(A) \land Box(B) \land Banana(C) \land \lnot On(monkey, box) \land \lnot Hold(monkey, banana)} \\ &{\bf{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\Downarrow monkeygoto(A, B)}} \\ &{Monkey(B) \land Box(B) \land Banana(C) \land \lnot On(monkey, box) \land \lnot Hold(monkey, banana)} \\ &{\bf{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\Downarrow movebox(B, C)}} \\ &{Monkey(C) \land Box(C) \land Banana(C) \land \lnot On(monkey, box) \land \lnot Hold(monkey, banana)} \\ &{\bf{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\Downarrow climbonto}} \\ &{Monkey(C) \land Box(C) \land Banana(C) \land On(monkey, box) \land \lnot Hold(monkey, banana)} \\ &{\bf{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\Downarrow hold}} \\ &{Monkey(C) \land Box(C) \land Banana(C) \land On(monkey, box) \land Hold(monkey, banana)} \\ \end{aligned} \\ Monkey(A)Box(B)Banana(C)¬On(monkey,box)¬Hold(monkey,banana)                                                       monkeygoto(A,B)Monkey(B)Box(B)Banana(C)¬On(monkey,box)¬Hold(monkey,banana)                                                       movebox(B,C)Monkey(C)Box(C)Banana(C)¬On(monkey,box)¬Hold(monkey,banana)                                                       climbontoMonkey(C)Box(C)Banana(C)On(monkey,box)¬Hold(monkey,banana)                                                       holdMonkey(C)Box(C)Banana(C)On(monkey,box)Hold(monkey,banana)

上述推理过程非常简单,但我们现在需要让计算机能够自动地实现这个过程,即让计算机实现自动推理,也就是说,如果初始状态不是题目假设的初始状态(猴子在A处,箱子在B处,香蕉在C处),而是满足实际情况的任一初始状态,我们的程序也可以通过自动推理得到最终结果。

为了达到上述目的,我们可以利用递归的方式来实现自动推理过程,我的思路通过伪代码的形式展示如下:

nextStep(第 i 个状态):
	IF 猴子不与箱子在同一位置:
		猴子走到箱子的位置
	ELSE 猴子与箱子在同一位置:
		IF 猴子不在箱子上:
			IF 猴子及箱子的位置与香蕉的位置不同:
				猴子把箱子推到香蕉的位置
			ELSE 猴子及箱子的位置与香蕉的位置相同:
				猴子爬上箱子
		ELSE 猴子在箱子上:
			IF 猴子及箱子的位置与香蕉的位置不同:
				猴子爬下箱子
			ELSE 猴子及箱子的位置与香蕉的位置相同:
				猴子拿到香蕉
	nextStep(第 i+1 个状态)

可以看到在上面的伪代码中,我们在执行每个操作之前,都需要检查当前状态是否可以满足该操作的先决条件,如果满足,就执行相应的操作,否则检查下一个操作所要求的先决条件。

伪代码可以帮助我们理清思路,接下来用实际代码实现这个过程:

def nextStep(i, States, routesave):
    '''
    msg: 递归求解
    param {
        i: int, 表示当前处于求解过程中的第几个状态
        States: list, 表示对推理过程中所有状态的记录
        routesave: list, 表示对推理过程中所执行的操作的记录
    }
    '''
    # 如果满足条件,就输出结果
    if States[i].mholdban == 1:
        showSolution(routesave, i - 1)
        exit(0)

    States.insert(i + 1, copyState(States[i]))  # 通过是上一个状态深拷贝生成当前状态

    if States[i].monkey != States[i].box:  # 猴子不与箱子在同一位置
        States[i + 1].monkeygoto(States[i].box, routesave, i)  # 猴子走到箱子的位置
    else:  # 猴子与箱子在同一位置
        if States[i].monbox == -1:  # 猴子不在箱子上
            if States[i].monkey != States[i].banana:  # 猴子及箱子的位置与香蕉的位置不同
                States[i + 1].movebox(States[i].banana, routesave, i)  # 猴子把箱子推到香蕉的位置
            else:  # 猴子及箱子的位置与香蕉的位置相同
                States[i + 1].climbonto(routesave, i)  # 猴子爬上箱子
        else:  # 猴子在箱子上
            if States[i].monkey != States[i].banana:  # 猴子及箱子的位置与香蕉的位置不同
                States[i + 1].climbdown(routesave, i)  # 猴子爬下箱子
            else:  # 猴子及箱子的位置与香蕉的位置相同
                States[i + 1].hold(routesave, i)  # 猴子拿到香蕉
    
    nextStep(i + 1, States, routesave)  # 递归推理下一个状态

可以看到在上述代码中,相较于伪代码,我们还需要一个能够输出最终推理过程的函数:

def showSolution(routesave, i):
    '''
    msg: 打印所执行的所有的操作的记录
    '''
    print("\nResult to problem:\n")
    for c in range(i + 1):
        print("Step %d : %s \n" % (c + 1, routesave[c]))

并且在程序中,我们需要把上一个状态复制给下一个状态,然后在对新状态(下一个状态)做相应的修改。但是如果直接把第 i 个状态赋给第 i+1 个状态,即 States[i + 1] = States[i],则结果会出错。经调试可以发现当改变 States[i+1] 时,同时也会改变 States[i]。这是为什么呢?这实际上是一个深浅拷贝的问题:由于我们 States 列表中存放的每一项是一个类的实例对象,当我们进行 States[i + 1] = States[i] 赋值操作时,实际上是进行了浅拷贝,使两个状态指向了同一地址空间,所以在对其中一个修改后,另外一个也会发生相应改变。因此为了避免浅拷贝的问题,我们需要定义一个深拷贝函数来用于状态复制,其具体实现如下:

def copyState(source):
    '''
    msg: 用来复制前一个状态情况给当前状态,需注意的是,这里不能将对象直接复制,而必须创建新的对象复制原状态的数据即深拷贝
    '''
    state = State()
    state.monkey = source.monkey
    state.box = source.box
    state.banana = source.banana
    state.monbox = source.monbox
    state.mholdban = source.mholdban
    return state

5 检错机制

到上一步,其实整个推理过程的功能我们基本都已经实现了,但还不够完善,我们需要在我们的推理过程中进行错误检测,一旦发现错误,我们需要及时停止推理,因此我们应该在 nextStep(i, States, routesave) 函数中加入相应的检错机制:

# 检错机制
if i >= 150:  # 推理步数过长,推理有误
    print("%s\n" % "Step reached 150, have problem ")
    exit(0)
if States[i].monbox == 1 and States[i].monkey != States[i].box:  # 猴子在箱子上,但猴子和箱子的位置却不同,推理有误
    print("%s\n" % "State error, have problem ")
    exit(0)
if States[i].mholdban == 1 and States[i].monkey != States[i].banana:  # 猴子拿到香蕉,但猴子和香蕉的位置却不同,推理有误
    print("%s\n" % "State error, have problem ")
    exit(0)

6 组合模块形成完整代码

最后,我们将前面的所有模块进行组合,并添加 main 函数,得到完整代码:

# -*- coding: utf-8 -*-
'''
Description: 自己动手写确定性知识系统(一)——初步搭建谓词逻辑表示框架
Author: stepondust
Date: 2020-11-19
'''


class State:
    '''
    msg: 对状态和动作进行封装
    param {
        monkey: int, 表示猴子 monkey 位于何处
        box: int, 表示箱子 box 位于何处
        banana: int, 表示香蕉 banana 位于何处
        monbox: int, 表示猴子 monkey 是否在箱子 box 上
        mholdban: int, 表示猴子 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

    def monkeygoto(self, target, routesave, i):
        '''
        msg: 猴子 monkey 从 self.monkey 处走到 target 处
        param {
            target: int, 表示移动的目标
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, f"Monkey go to {transform(target)} from {transform(self.monkey)}")
        self.monkey = target

    def movebox(self, target, routesave, i):
        '''
        msg: 猴子 monkey 把箱子 box 从 self.monkey 处推到 target 处
        param {
            target: int, 表示移动的目标
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, f"Monkey move box to {transform(target)} from {transform(self.monkey)}")
        self.monkey = target
        self.box = target

    def climbonto(self, routesave, i):
        '''
        msg: 猴子 monkey 爬上箱子 box
        param {
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, "Monkey climb onto the box")
        self.monbox = 1

    def climbdown(self, routesave, i):
        '''
        msg: 猴子 monkey 爬下箱子 box
        param {
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, "Monkey climb down from the box")
        self.monbox = -1

    def hold(self, routesave, i):
        '''
        msg: 猴子 monkey 拿到香蕉 banana
        param {
            routesave: list, 表示对推理过程中所执行的操作的记录
            i: int, 表示当前处于求解过程中的第几个状态
        }
        '''
        routesave.insert(i, "Monkey hold the banana")
        self.mholdban = 1


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"


def copyState(source):
    '''
    msg: 用来复制前一个状态情况给当前状态,需注意的是,这里不能将对象直接复制,而必须创建新的对象复制原状态的数据即深拷贝
    '''
    state = State()
    state.monkey = source.monkey
    state.box = source.box
    state.banana = source.banana
    state.monbox = source.monbox
    state.mholdban = source.mholdban
    return state


def showSolution(routesave, i):
    '''
    msg: 打印所执行的所有的操作的记录
    '''
    print("\nResult to problem:\n")
    for c in range(i + 1):
        print("Step %d : %s \n" % (c + 1, routesave[c]))


def nextStep(i, States, routesave):
    '''
    msg: 递归求解
    param {
        i: int, 表示当前处于求解过程中的第几个状态
        States: list, 表示对推理过程中所有状态的记录
        routesave: list, 表示对推理过程中所执行的操作的记录
    }
    '''
    # 检错机制
    if i >= 150:  # 推理步数过长,推理有误
        print("%s\n" % "Step reached 150, have problem ")
        exit(0)
    if States[i].monbox == 1 and States[i].monkey != States[i].box:  # 猴子在箱子上,但猴子和箱子的位置却不同,推理有误
        print("%s\n" % "State error, have problem ")
        exit(0)
    if States[i].mholdban == 1 and States[i].monkey != States[i].banana:  # 猴子拿到香蕉,但猴子和香蕉的位置却不同,推理有误
        print("%s\n" % "State error, have problem ")
        exit(0)

    # 如果满足条件,就输出结果
    if States[i].mholdban == 1:
        showSolution(routesave, i - 1)
        exit(0)

    States.insert(i + 1, copyState(States[i]))  # 通过是上一个状态深拷贝生成当前状态

    if States[i].monkey != States[i].box:  # 猴子不与箱子在同一位置
        States[i + 1].monkeygoto(States[i].box, routesave, i)  # 猴子走到箱子的位置
    else:  # 猴子与箱子在同一位置
        if States[i].monbox == -1:  # 猴子不在箱子上
            if States[i].monkey != States[i].banana:  # 猴子及箱子的位置与香蕉的位置不同
                States[i + 1].movebox(States[i].banana, routesave, i)  # 猴子把箱子推到香蕉的位置
            else:  # 猴子及箱子的位置与香蕉的位置相同
                States[i + 1].climbonto(routesave, i)  # 猴子爬上箱子
        else:  # 猴子在箱子上
            if States[i].monkey != States[i].banana:  # 猴子及箱子的位置与香蕉的位置不同
                States[i + 1].climbdown(routesave, i)  # 猴子爬下箱子
            else:  # 猴子及箱子的位置与香蕉的位置相同
                States[i + 1].hold(routesave, i)  # 猴子拿到香蕉
    
    nextStep(i + 1, States, routesave)  # 递归推理下一个状态


if __name__ == "__main__":
    s = input("Please input init state [monkey(-1/0/1), box(-1/0/1), banana(-1/0/1), ifMonkeyIsOnBox(-1/1), ifMonkeyHoldBanana(-1/1)]: ")
    states = s.split(" ")
    state = State(int(states[0]), int(states[1]), int(states[2]), int(states[3]), int(states[4]))
    States = [None] * 150
    routesave = [None] * 150
    States.insert(0, state)
    nextStep(0, States, routesave)

我们对上述代码进行测试,可以发现我们的程序能够适用于实际情况中的任一初始状态,自动推理实现成功:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
好了本文到此就快结束了,以上就是我自己动手用谓词逻辑表示方法解决猴子香蕉问题的想法和思路,大家参照一下即可,重要的还是经过自己的思考来编写代码,文章中还有很多不足和不正确的地方,欢迎大家指正(也请大家体谅,写一篇博客真的挺累的,花的时间比我写出代码的时间还要长),我会尽快修改,之后我也会尽量完善本文,尽量写得通俗易懂。

博文创作不易,转载请注明本文地址:https://blog.youkuaiyun.com/qq_44009891/article/details/109750752

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值