广州大学人工智能实验一-谓词的表示与知识推理实验

广州大学学生实验报告

开课学院及实验室: 计算机科学与网络工程学院电子信息楼513      2023年11月26日

学院

计算机科学与网络工程学院

年级/专业/班

****** 

姓名

****

学号

****

实验课程名称

人工智能原理实验

成绩

实验项目名称

实验一  谓词的表示与知识推理实验

指导老师

****

实验一  谓词的表示与知识推理实验

  • 实验目的

本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等有比较深入的认识。本实验通过不同知识的表达与推理问题,强化学生对知识表示的了解和应用,为人工智能后续环节的课程奠定基础。

二、基本要求

  1. 实验前,复习《人工智能原理》课程中的有关内容。
  2. 准备好实验数据。
  3. 编程或验证需要独立完成,程序应加适当的注释。
  4. 完成实验报告。

三、实验软件

使用C或C++(Visual studio平台等),或其它语言,如matlab或Python。

四、实验内容:

  • 猴子摘香蕉问题

利用一阶谓词逻辑求解猴子摘香蕉问题:房内有一个猴子,一个箱子,天花板上挂了一串香蕉,其位置如图1所示,猴子为了拿到香蕉,它必须把箱子搬到香蕉下面,然后再爬到箱子上。请定义必要的谓词,列出问题的初始化状态(即下图所示状态),目标状态(猴子拿到了香蕉,站在箱子上,箱子位于位置b)。(附加:从初始状态到目标状态的谓词演算过程。) 

图1

定义描述环境状态的谓词。

AT(x,w):x在w处,个体域:x?{monkey},w?{a,b,c,box};

HOLD(x,t):x手中拿着t,个体域:t?{box,banana};

EMPTY(x):x手中是空的;

ON(t,y):t在y处,个体域:y?{b,c,ceiling};

CLEAR(y):y上是空的;

BOX(u):u是箱子,个体域:u?{box};

BANANA(v):v是香蕉,个体域:v?{banana};

使用谓词、连结词、量词来表示环境状态。

问题的初始状态可表示为:So:AT(monkey,a)?EMPTY(monkey)?ON(box,c)?ON(banana,ceiling)?CLEAR(b)?BOX(box)?

BANANA(banana)

要达到的目标状态为:Sg:

AT(monkey,box)?HOLD(monkey,banana)?ON(box,b)?CLEAR(ceiling)?CLEAR(c)?

BOX(box)?BANANA(banana)

从初始状态到目标状态的转化, 猴子需要完成一系列操作, 定义操作类谓词表示其动作。

WALK(m,n):猴子从m走到n处,个体域:m,n?{a,b,c};

CARRY(s,r):猴子在r处拿到s,个体域:r?{c,ceiling},s?{box,banana};

CLIMB(u,b):猴子在b处爬上u;

这3个操作也可分别用条件和动作来表示。条件直接用谓词公式表示,是为完成相应操作所必须具备的条件;当条件中的事实使其均为真时,则可激活操作规则,于是可执行该规则中的动作部分。动作通过前后状态的变化表示,即通过从动作前删除或增加谓词公式来描述动作后的状态。

WALK(m,n):猴子从m走到n处

条件:AT(monkey,m)

动作:

CARRY(s,r):猴子在r处拿到s

条件:AT(monkey,r)?EMPTY(monkey)?ON(s,r)?BOX(box)?BANANA(banana)

动作:

CLIMB(u,b):猴子在b处爬上u

条件:AT(monkey,b)?HOLD(monkey,u)?CLEAR(b)?BOX(box)?BANANA(banana)

动作:

按照行动计划, 一步步进行状态替换, 直至目标状态

AT(monkey,a)?EMPTY(monkey)?ON(box,c)?ON(banana,ceiling)?CLEAR(b)?BOX(box)?

BANANA(banana)

AT(monkey,c)?EMPTY(monkey)?ON(box,c)?ON(banana,ceiling)?CLEAR(b)?BOX(box)?

BANANA(banana)

AT(monkey,c)?HOLD(monkey,box)?ON(banana,ceiling)?CLEAR(b)?CLEAR(c)?BOX(box)?

BANANA(banana)

AT(monkey,b)?HOLD(monkey,box)?ON(banana,ceiling)?CLEAR(b)?CLEAR(c)?BOX(box)?

BANANA(banana)

AT(monkey,box)?EMPTY(monkey)?ON(box,b)?ON(banana,ceiling)?CLEAR(c)?BOX(box)?

BANANA(banana)

AT(monkey,box)?HOLD(monkey,banana)?ON(box,b)?CLEAR(ceiling)?CLEAR(c)?BOX(box)?

BANANA(banana)(目标得解)

猴子行动的规则序列是:WALK(a,c)→CARRY(c,box)→WALK(c,b)→CLIMB(box,b)→

CARRY(banana,ceiling)

参考代码:

#include <stdio.h>

struct State

{

int monkey; /*-1:Monkey at A;0: Monkey at B;1:Monkey at C;*/

    int box; /*-1:box at A;0:box at B;1:box at C;*/

    int banana; /*Banana at B,Banana=0*/

    int monbox; /*-1: monkey on the box;1: monkey  the box;*/

};

struct State States [150];

char* routesave[150];

/*function monkeygoto,it makes the monkey goto the other place*/

void monkeygoto(int b,int i)

{  

int a;

a=b;

if (a==-1)

{

routesave[i]="Monkey go to A";

States[i+1]=States[i];

States[i+1].monkey=-1;

}

else if(a==0)

{

routesave[i]="Monkey go to B";

States[i+1]=States[i];

States[i+1].monkey=0;

}

else if(a==1)

{

routesave[i]="Monkey go to C";

States[i+1]=States[i];

States[i+1].monkey=1;

}

else

    {

printf("parameter is wrong");

}

}

/*end function monkeyygoto*/

/*function movebox,the monkey move the box to the other place*/

void movebox(int a,int i)

{  

int B;

B=a;

if(B==-1)

{

routesave[i]="monkey move box to A";

States[i+1]=States[i];

States[i+1].monkey=-1;

States[i+1].box=-1;

}

else if(B==0)

{

routesave[i] = "monkey move box to B";

States[i+1]=States[i];

States[i+1].monkey=0;

States[i+1].box=0;

}

else if(B==1)

{

routesave[i] = "monkey move box to C";

States[i+1]=States[i];

States[i+1].monkey=1;

States[i+1].box=1;

}

else

{

printf("parameter is wrong");

}

}

/*end function movebox*/

/*function climbonto,the monkey climb onto the box*/

void climbonto(int i)

{

routesave[i]="Monkey climb onto the box";

States[i+1]=States[i];

States[i+1].monbox=1;

}

/*function climbdown,monkey climb down from the box*/

void climbdown(int i)

{  

routesave[i]="Monkey climb down from the box";

States[i+1]=States[i];

States[i+1].monbox=-1;

}

/*function reach,if the monkey,box,and banana are at the same place,the monkey reach banana*/

void reach(int i)

{   

routesave[i]="Monkey reach the banana";

}

/*output the solution to the problem*/

void showSolution(int i)

{

    int c;

    printf ("%s \n", "Result to problem:");

    for(c=0; c<i+1; c++)

    {

printf ("Step %d : %s \n",c+1,routesave[c]);

    }

    printf("\n");

}

/*perform next step*/

void nextStep(int i)

{

int c;

    int j;

    if(i>=150)

    {

printf("%s  \n", "steplength reached 150,have problem ");

        return;

    }

for (c=0; c<i; c++) /*if the current state is same to previous,retrospect*/

    {

if(States[c].monkey==States[i].monkey&&States[c].box==States[i].box&&States[c].banana==States[i].banana&&States[c].monbox==States[i].monbox)

        {

return;

        }

    }

    if(States[i].monbox==1&&States[i].monkey==0&&States[i].banana==0&&States[i].box==0)

    {

showSolution(i);

printf("Press any key to continue \n");

getchar();/*to save screen for user,press any key to continue*/

return;

}  

j=i+1;    

if(States[i].monkey==0)

{

if(States[i].box==0)

{

if(States[i].monbox==-1)

{

climbonto(i);

reach(i+1);

nextStep(j);

/*monkeygoto(-1,i);

nextStep(j);

monkeygoto(0,i);

nextStep(j);

movebox(-1,i);

nextStep(j);

movebox(0,i);

nextStep(j);*/

}

else

{

reach(i+1);

nextStep(j);

/*climbdown(i);

nextStep(j);*/

}

}

else if(States[i].box==1)

{

/*monkeygoto(-1,i);

nextStep(j);*/

monkeygoto(1,i);

nextStep(j);

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

else /*box==-1*/

{

monkeygoto(-1,i);

nextStep(j);

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

    }

/*end if*/

if(States[i].monkey==-1)

    {

if(States[i].box==-1)

{

if(States[i].monbox==-1)

{

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

else

{

climbdown(i);

nextStep(j);

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

}

else if(States[i].box==0)

{

monkeygoto(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

else

{

monkeygoto(1,i);

nextStep(j);

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

}

/*end if*/

if(States[i].monkey==1)

    {

if (States[i].box==1)

{

if(States[i].monbox==-1)

{     

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

else

{

climbdown(i);

nextStep(j);

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);          

}

}

else if(States[i].box==-1)

{        

monkeygoto(-1,i);

nextStep(j);

movebox(0,i);

nextStep(j);

  movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

else

{

monkeygoto(0,i);

nextStep(j);

movebox(0,i);

nextStep(j);

climbonto(i);

reach(i+1);

nextStep(j);

}

    }

/*end if*/

}/*end nextStep*/

int main()

{

    States[0].monkey=-1;

    States[0].box=1;

    States[0].banana=0;

    States[0].monbox=-1;

    nextStep(0);

}

  • 传教士(牧师)与野人问题

问题描述:

有n个牧师和n个野人准备渡河,但只有一条能容纳c个人的小船,为了防止野人侵犯牧师,要求无论在何处,牧师的人数不得少于野人的人数(除非牧师人数为0),且假定野人与牧师都会划船,试设计一个算法,确定他们能否渡过河去,若能,则给出小船来回次数最少的最佳方案。

实验步骤:

输入:牧师人数(即野人人数):n;小船一次最多载人量:c。  

输出:若问题无解,则显示Failed,否则,显示Successed输出所有可行方案,并标注哪一组是最佳方案。用三元组(X1, X2, X3)表示渡河过程中的状态。并用箭头连接相邻状态以表示迁移过程:初始状态->中间状态->目标状态。   

例:当输入n=2,c=2时,输出:221->200->211->010->021->000;  

其中:X1表示起始岸上的牧师人数;X2表示起始岸上的野人人数;X3表示小船现在位置(1表示起始岸,0表示目的岸)。

要求:写出算法的设计思想和源程序,并有用户界面实现人机交互(控制台或者窗口都可以),进行输入和输出结果,如:  

Please input n: 2         Please input c: 2

Optimal Procedure: 221->200->211->010->021->000

Successed or Failed?: Successed     

3汉诺塔问题

汉诺塔问题来自一个古老的传说:在世界刚被创建的时候有一座钻石宝塔(塔A),其上有64个金碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着这座塔有另外两个钻石宝塔(塔B和塔C)。从世界创始之日起,婆罗门的牧师们就一直在试图把塔A上的碟子移动到塔C上去,其间借助于塔B的帮助。每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。当牧师们完成任务时,世界末日也就到了。

  采用问题归约法把汉诺塔问题分成以下三个步骤实现:

  1. 将塔A上的n-1个碟子借助塔C先移到塔B上
  2. 把塔A上剩下的一个碟子移到塔C上
  3. 把n-1个碟子从塔B借助于塔A移到塔C上

实验要求: 

1.让盘子数从2 开始到7进行实验,记录程序运行时间和递归调用次数。

2.画出盘子数n和运行时间t 、递归调用次数m的关系图,并进行分析

五、学生实验报告要求

    1. 对于猴子摘香蕉问题根据自己编写代码或者参考代码,用不同的初始状态测试代码,记录每种初始状态下的输出结果,并对每个结果进行解释。
    2. 完善猴子摘香蕉问题参考代码,参考代码中有什么问题?应该如何修改会更好。
    3. 传教士(牧师)与野人问题,写出状态表示的数据结构,还有每种解的状态迁移图示。
    4. 写出传教士(牧师)与野人问题的程序清单(语言不限)
    5. 实验结果讨论。

实验报告

一 猴子摘香蕉问题

  1. 对于猴子摘香蕉问题根据自己编写代码或者参考代码,用不同的初始状态测试代码,记录每种初始状态下的输出结果,并对每个结果进行解释。

分析1  : 初始的时候, 猴子在A, 箱子在C,

步骤 : 所以, 猴子移动到C, 把箱子移动到B, 爬上箱子, 拿到香蕉

分析2 : 初始的时候, 猴子在A, 箱子在B

步骤 : 猴子移动到B, 爬上箱子, 拿到香蕉

分析3 : 初始的时候猴子在A, 箱子在A

步骤 : 猴子将箱子移动到B, 爬上箱子, 就能拿到香蕉

分析4 : 初始的时候, 猴子在B, 箱子在C

步骤 : 猴子移动C, 把箱子移动到B, 猴子爬上箱子, 拿到香蕉

  1. 完善猴子摘香蕉问题参考代码,参考代码中有什么问题?应该如何修改会更好。

问题1 : 代码的逻辑错误

错误2 :

  1. 实验代码
#include <stdio.h>

#include <time.h>

#include <stdlib.h>



struct State

{

    int monkey;  // -1: 猴子在A; 0: 猴子在B; 1: 猴子在C;

    int box;     // -1: 箱子在A; 0: 箱子在B; 1: 箱子在C;

    int banana;  // 香蕉在B, banana = 0

    int monbox;  // -1: 猴子在箱子上; 1: 猴子在箱子里;

};



struct State States[150];

char *routesave[150];



// 猴子移动函数,使猴子移到另一个位置

void monkeygoto(int b, int i)

{

    int a;

    a = b;

    if (a == -1)

    {

        routesave[i] = "猴子移动到A";

        States[i + 1] = States[i];

        States[i + 1].monkey = -1;

    }

    else if (a == 0)

    {

        routesave[i] = "猴子移动到B";

        States[i + 1] = States[i];

        States[i + 1].monkey = 0;

    }

    else if (a == 1)

    {

        routesave[i] = "猴子移动到C";

        States[i + 1] = States[i];

        States[i + 1].monkey = 1;

    }

    else

    {

        printf("参数错误");

    }

}



// 移动箱子函数,猴子将箱子移到另一个位置

void movebox(int a, int i)

{

    int B;

    B = a;

    if (B == -1)

    {

        routesave[i] = "猴子将箱子移动到A";

        States[i + 1] = States[i];

        States[i + 1].monkey = -1;

        States[i + 1].box = -1;

    }

    else if (B == 0)

    {

        routesave[i] = "猴子将箱子移动到B";

        States[i + 1] = States[i];

        States[i + 1].monkey = 0;

        States[i + 1].box = 0;

    }

    else if (B == 1)

    {

        routesave[i] = "猴子将箱子移动到C";

        States[i + 1] = States[i];

        States[i + 1].monkey = 1;

        States[i + 1].box = 1;

    }

    else

    {

        printf("参数错误");

    }

}



// 爬上箱子函数,猴子爬上箱子

void climbonto(int i)

{

    routesave[i] = "猴子爬上箱子";

    States[i + 1] = States[i];

    States[i + 1].monbox = 1;

}



// 从箱子上爬下函数,猴子从箱子上爬下

void climbdown(int i)

{

    routesave[i] = "猴子从箱子上爬下";

    States[i + 1] = States[i];

    States[i + 1].monbox = -1;

}



// 达到目标函数,如果猴子、箱子和香蕉在同一位置,猴子达到香蕉

void reach(int i)

{

    routesave[i] = "猴子达到香蕉";

}



// 输出问题的解决方案

void showSolution(int i)

{

    int c;

    printf("%s \n", "问题的解决方案:");

    for (c = 0; c < i + 1; c++)

    {

        printf("步骤 %d : %s \n", c + 1, routesave[c]);

    }

    printf("\n");

}



// 执行下一步

void nextStep(int i)

{

    int c;

    int j;

    if (i >= 150)

    {

        printf("%s  \n", "步长达到150,存在问题");

        return;

    }

    for (c = 0; c < i; c++) // 如果当前状态与前一状态相同,则回溯

    {

        if (States[c].monkey == States[i].monkey && States[c].box == States[i].box && States[c].banana == States[i].banana && States[c].monbox == States[i].monbox)

        {

            return;

        }

    }

    if (States[i].monbox == 1 && States[i].monkey == 0 && States[i].banana == 0 && States[i].box == 0)

    {

        showSolution(i);

        printf("按任意键继续 \n");

        getchar(); // 为了保存用户屏幕,按任意键继续

        return;

    }

    j = i + 1;

    if (States[i].monkey == 0)

    {

        if (States[i].box == 0)

        {

            if (States[i].monbox == -1)

            {

                climbonto(i);

                reach(i + 1);

                nextStep(j);

            }

            else

            {

                reach(i + 1);

                nextStep(j);

            }

        }



        else if (States[i].box == 1)

        {

            monkeygoto(1, i);

            nextStep(j);

            // movebox(0, i);

            // nextStep(j);

            // climbonto(i);

            // reach(i + 1);

            // nextStep(j);

        }

        else

        {

            monkeygoto(-1, i);

            nextStep(j);

            // movebox(0, i);

            // nextStep(j);

            // climbonto(i);

            // reach(i + 1);

            // nextStep(j);

        }

    }

    else if (States[i].monkey == -1)

    {

        if (States[i].box == -1)

        {

            if (States[i].monbox == -1)

            {

                movebox(0, i);

                nextStep(j);

                // climbonto(i);

                // reach(i + 1);

                // nextStep(j);

            }

            else

            {

                climbdown(i);

                nextStep(j);

                // movebox(0, i);

                // nextStep(j);

                // climbonto(i);

                // reach(i + 1);

                // nextStep(j);

            }

        }

        else if (States[i].box == 0)

        {

            monkeygoto(0, i);

            nextStep(j);

            // climbonto(i);

            // reach(i + 1);

            // nextStep(j);

        }

        else //第一步

        {

            monkeygoto(1, i);

            nextStep(j);

            // movebox(0, i);

            // nextStep(j);

            // climbonto(i);

            // reach(i + 1);

            // nextStep(j);

        }

    }

    else if (States[i].monkey == 1)

    {

        if (States[i].box == 1)

        {

            if (States[i].monbox == -1) //第二部

            {

                movebox(0, i);

                nextStep(j);

                // climbonto(i);

                // reach(i + 1);

                // nextStep(j);

            }

            else

            {

                climbdown(i);

                nextStep(j);

                // movebox(0, i);

                // nextStep(j);

                // climbonto(i);

                // reach(i + 1);

                // nextStep(j);

            }

        }

        else if (States[i].box == -1)

        {

            monkeygoto(-1, i);

            nextStep(j);

            // movebox(0, i);

            // nextStep(j);

            // movebox(0, i);

            // nextStep(j);

            // climbonto(i);

            // reach(i + 1);

            // nextStep(j);

        }

        else

        {

            monkeygoto(0, i);

            nextStep(j);

            // movebox(0, i);

            // nextStep(j);

            // climbonto(i);

            // reach(i + 1);

            // nextStep(j);

        }

    }

}



int main()

{

    States[0].monkey = -1;

    States[0].box = 1;

    States[0].banana = 0;

    States[0].monbox = -1;

   

    srand(time(0));



    // States[0].monkey = rand() % 3 - 1; //取值范围 (-1, 1)

    // States[0].box = rand() % 3 - 1; //取值范围 (-1, 1)

    // States[0].banana = 0; //注意 : 香蕉的位置固定在B点

    // States[0].monbox = -1;



    printf("猴子在%c ", States[0].monkey + 'A' + 1);

    printf("箱子在%c ", States[0].box + 'A' + 1);

    printf("香蕉在%c\n", States[0].banana + 'A' + 1);

   

   

    nextStep(0);

    return 0;

}

二 传教士和野人问题

  1. 传教士(牧师)与野人问题,写出状态表示的数据结构,还有每种解的状态迁移图示。

struct State

{

    int M;    // 左岸传教士

    int C;    // 左岸野人

    int side; // 船的位置

};

状态迁移图示

  1. 写出传教士(牧师)与野人问题的程序清单(语言不限)

程序清单 :

bool  isRepeat() : 判断状态是否重复

boold isEnd() :  : 判断是否已经成功过河

bool isValid() : 判断当前船的载人方案是否合法(使得传教士人数大于野人, 保证传教士的安全)

void dfs(int) : 递归函数 : 船载人过河

int main() : 主函数 : 调用dfs()进程过河载人方案的求解

  1. 实验运行结果

3个野人/传教士, 船载人数 : 2

结果 : 成功

2个野人/传教士, 船载人数 : 2

结果 : 成功

3个野人/传教士, 船载人数 : 3

结果 : 成功

3个野人/传教士, 船载人数 : 1

结果 : 失败

实验完整源代码 :

#include <iostream>
#include <vector>
#include <set>
#include <unordered_set>
using namespace std;
int mc = 3;           // 传教士和野人的数目
int boat = 2;         // 船承载的人数
bool success = false; // 成功求解?

// 岸的左边的状态
struct State
{
    int M;    // 传教士
    int C;    // 野人
    int side; // 船的位置

    // 无参构造函数
    State()
    {
    }

    // 有参构造函数
    State(int m, int c, int side)
    {
        M = m;
        C = c;
        this->side = side;
    }
};
vector<vector<State *>> ans; // 保存所有路径
// State *state = new State(mc, mc, 0);  // 初始状态, 不能在这里初始化
State *state;                         // 初始状态
State *endState = new State(0, 0, 1); // 结束状态
vector<State *> path;                 // 保存路径
vector<State *> st;                   // 用于判断状态是否重复,

/**
 * 状态是否重复
 */
bool isRepeat()
{
    int n = st.size();
    for (auto state2 : st)
    {
        if (state->M == state2->M && state->C == state2->C && state->side == state2->side)
        {
            return true;
        }
    }
    return false;
}

// 结束状态判断
bool isEnd()
{
    // cout << "isEnd : " << state->M << ' ' << state->C << " " << state->side << endl;

    if (state->M == 0 && state->C == 0)
    {
        // 全部人移动到右边
        return true;
    }

    return false;
}

/**
 * 判断状态是否合法 :
 * 1. M == C
 * 2. M == 0
 * 3. M == mc //
 * 其他情况如果M > C, 那么另外一边就会出现 M < C, 所以都不合法
 *
 */
bool isValid()
{
    if (state->M == 0 || state->M == mc || state->M == state->C)
    {
        return true;
    }
    return false;
}
void dfs(int u)
{

    // 递归结束
    if (isEnd())
    {
        success = true;
        cout << "步骤如下:\n";
        ans.push_back(path);
        for (int i = 0; i < path.size(); i++)
        {
            State *state2 = path[i];

            cout << state2->M << state2->C << state2->side;

            if (i != path.size() - 1)
                cout << " -> ";
        }
        cout << endl;
        return;
    }

    // 进行载人

    // 船在左边
    if (state->side == 0)
    {
        for (int i = 0; i <= boat && i <= state->M; i++) // 传教士的人数
        {
            for (int j = 0; j <= boat - i && j <= state->C; j++) // 野人的人数
            {
                if (i + j == 0)
                    continue;  // 不能一个人也不在船
                state->M -= i; // 人数减少
                state->C -= j;
                state->side = 1;

                if (isValid() && !isRepeat())
                {
                    // 保存路径
                    State *state2 = new State(state->M, state->C, state->side); // 要创建一个新的指针, 否则出错
                    path.push_back(state2);
                    st.push_back(state2);
                    dfs(u + 1);
                    path.pop_back(); // 弹出
                    st.pop_back();
                }

                // 恢复现场
                state->M += i;
                state->C += j;
                state->side = 0;
            }
        }
    }

    // 船在右边, 左边的人数增加
    else if (state->side == 1)
    {
        for (int i = 0; i <= boat && i <= mc - state->M; i++) // 传教士的人数
        {
            for (int j = 0; j <= boat - i && j <= mc - state->C; j++) // 野人的人数
            {
                if (i + j == 0)
                    continue;  // 不能一个人也不在船
                state->M += i; // 人数增加
                state->C += j;
                state->side = 0;
                if (isValid() && !isRepeat())
                {
                    State *state2 = new State(state->M, state->C, state->side);
                    path.push_back(state2); // 要创建一个新的指针, 否则出错
                    st.push_back(state2);
                    dfs(u + 1);
                    path.pop_back();
                    st.pop_back();
                }

                // 恢复现场
                state->M -= i; // 人数增加
                state->C -= j;
                state->side = 1;
            }
        }
    }
}
int main()
{
    cout << "请输入传教士和野人的数目" << endl;
    cin >> mc;
    cout << "请输入小船最多载人量" << endl;
    cin >> boat;
    // 初始化
    state = new State(mc, mc, 0); // 初始状态

    State *state2 = new State(state->M, state->C, state->side); // 要创建一个新的指针, 否则出错
    path.push_back(state2);

    st.push_back(state2); // 第一个状态已经使用过了
    dfs(0);

    // 如果没有成功, 输出失败
    if (!success)
    {
        cout << endl;
        cout << "失败, 没有任何一个方案能够求解这个问题" << endl;
    }
    else
    {
        cout << endl;
        cout << "succeed" << endl;
        int n = ans.size();
        int m = ans[0].size(); // 最优解的步数
        for (int i = 1; i < n; i++)
        {
            if (ans[i].size() < m)
            {
                m = ans[i].size();
            }
        }

        cout << "最好的方案是" << endl;
        for (int i = 0; i < n; i++)
        {

            if (ans[i].size() == m)
            {
                auto best = ans[i];
                for (int i = 0; i < m; i++)
                {
                    cout << best[i]->M << best[i]->C << best[i]->side;
                    if (i != m - 1)
                        cout << " -> ";
                }
                cout << endl;
            }
        }
        cout << endl;
    }
    return 0;
}

汉诺塔问题 :

  1. 让盘子数从2 开始到7进行实验,记录程序运行时间和递归调用次数

  1. 画出盘子数n和运行时间t 、递归调用次数m的关系图,并进行分析

分析 : 随机盘子的数目的增加, 运行时间的上升趋势越来越快

  1. 实验源代码

  2. #include <iostream>
    #include <ctime>
    using namespace std;
    /**
     * 汉诺塔dfs函数
    */
    
    long long cnt = 0; //计数
    
    void dfs(int u, char source, char target, char tool) {
        cnt ++;
        if(u == 1) { //将单个盘子, 直接移动到第三个柱子
            cout << "将盘子1从" << source << "移动到" << target << endl;
        }
        else {
            dfs(u - 1, source, tool, target); //将上面的u - 1个盘子, A -> B, 借助 : C
            cout << "将盘子" << u << "从" << source << "移动到" << target << endl;
            dfs(u - 1, tool, target, source);
        }
    }
    int main() {
    
        int n = 7; 
        cout << "盘子个数 : " << n << endl;
        clock_t st, ed;
        st = clock();
        dfs(n, 'A', 'C', 'B');
    
        ed = clock();
        
        double time = (double)(ed - st) / CLOCKS_PER_SEC;
        cout << "递归次数 : " << cnt << endl;
        cout << "执行时间 : " << time << endl;
    }
    

  • 42
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值