广州大学人工智能原理实验一:知识的表示与推理实验
广州大学人工智能原理实验二:八数码问题
广州大学人工智能原理实验三:产生式系统推理
广州大学人工智能原理实验四:TSP问题的遗传算法实现
广州大学人工智能原理实验五:基于汉诺塔的问题规约图实现
五份实验报告下载链接
本实验课程是计算机、智能、物联网等专业学生的一门专业课程,通过实验,帮助学生更好地掌握人工智能相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对智能程序、智能算法等有比较深入的认识。本实验通过牧师与野人渡河的问题,强化学生对知识表示的了解和应用,为人工智能后续环节的课程奠定基础。
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};
2.使用谓词、连结词、量词来表示环境状态。
问题的初始状态可表示为: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©?
BOX(box)?BANANA(banana)
3.从初始状态到目标状态的转化, 猴子需要完成一系列操作, 定义操作类谓词表示其动作。
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)
动作:
4.按照行动计划, 一步步进行状态替换, 直至目标状态
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©?BOX(box)?
BANANA(banana)
AT(monkey,b)?HOLD(monkey,box)?ON(banana,ceiling)?CLEAR(b)?CLEAR©?BOX(box)?
BANANA(banana)
AT(monkey,box)?EMPTY(monkey)?ON(box,b)?ON(banana,ceiling)?CLEAR©?BOX(box)?
BANANA(banana)
AT(monkey,box)?HOLD(monkey,banana)?ON(box,b)?CLEAR(ceiling)?CLEAR©?BOX(box)?
BANANA(banana)(目标得解)
猴子行动的规则序列是:WALK(a,c)→CARRY(c,box)→WALK(c,b)→CLIMB(box,b)→
CARRY(banana,ceiling)
问题描述:
有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
#include
#include
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 NOT on the box;1: monkey on the box;*/
};
struct State States[150]; //记录步骤状态
char* routesave[150];
int Islegal(int num); /*判断输入的位置是否合法*/
void monkeygoto(int b, int i);/*移动函数*/
void movebox(int a, int i);/*推箱子*/
void climbonto(int i);/*爬上箱子*/
void climbdown(int i);/*跳下箱子*/
void reach(int i);/*摘到香蕉*/
void showSolution(int i);/*打印输出路线*/
void nextStep(int i); /*下一步,递归*/
int main()
{
//初始化状态
States[0].monkey = -1; //猴子的初始位置
States[0].box = 1; //盒子的初始位置
States[0].banana = 0; //香蕉的初始位置
States[0].monbox = -1; //猴子和箱子是否爬上了箱子
int a = Islegal(0); //判断输入是否合法
if (a)nextStep(0);
else printf("输入不合法 \n");
}
/*判断输入的位置是否合法*/
int Islegal(int num)
{
if (States[num].monkey < -1 || States[num].monkey >1)
return 0;
if (States[num].box < -1 || States[num].box >1)
return 0;
if (States[num].banana < -1 || States[num].banana >1)
return 0;
if (States[num].monbox !=-1 && States[num].monbox !=1)
return 0;
if (States[num].monbox == 1 && (States[num].monkey != States[num].box)) //猴子和箱子不在同一个位置上,不可能在箱子上的
return 0;
return 1;
}
/*移动函数*/
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");
}
}
/*推箱子*/
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");
}
}
/*爬上箱子*/
void climbonto(int i)
{
routesave[i] = "Monkey climb onto the box";
States[i + 1] = States[i];
States[i + 1].monbox = 1;
}
/*跳下箱子*/
void climbdown(int i)
{
routesave[i] = "Monkey climb down from the box";
States[i + 1] = States[i];
States[i + 1].monbox = -1;
}
/*摘到香蕉*/
void reach(int i)
{
routesave[i] = "Monkey reach the banana";
}
/*打印输出路线*/
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");
}
/*下一步,递归*/
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 (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 == States[i].box && States[i].banana == States[i].monkey) //这种情况说明摘到了香蕉,直接打印路径
{
reach(i);
showSolution(i);
printf("Press any key to continue \n");
getchar();
return;
}
j = i + 1;
if (States[i].monkey == 0) //针对猴子在0位置,盒子在另外三个位置的条件判断
{
if (States[i].box == 0) //香蕉和盒子合一,判断是否需要爬上去
{
if (States[i].monbox == -1)
{
climbonto(i);
nextStep(j);
monkeygoto(-1, i);
nextStep(j);
monkeygoto(1, i);
nextStep(j);
movebox(-1, i);
nextStep(j);
movebox(1, i);
nextStep(j);
}
else
{
climbdown(i);
nextStep(j);
}
}
else //盒子在别的位置
{
monkeygoto(-1, i);
nextStep(j);
monkeygoto(1, i);
nextStep(j);
}
}
if (States[i].monkey == -1)
{
if (States[i].box == -1)
{
if (States[i].monbox == -1)
{
climbonto(i);
nextStep(j);
monkeygoto(0, i);
nextStep(j);
monkeygoto(1, i);
nextStep(j);
movebox(0, i);
nextStep(j);
movebox(1, i);
nextStep(j);
}
else
{
climbdown(i);
nextStep(j);
}
}
else
{
monkeygoto(0, i);
nextStep(j);
monkeygoto(1, i);
nextStep(j);
}
}
if (States[i].monkey == 1)
{
if (States[i].box == 1)
{
if (States[i].monbox == -1)
{
climbonto(i);
nextStep(j);
monkeygoto(0, i);
nextStep(j);
monkeygoto(-1, i);
nextStep(j);
movebox(0, i);
nextStep(j);
movebox(-1, i);
nextStep(j);
}
else
{
climbdown(i);
nextStep(j);
}
}
else
{
monkeygoto(0, i);
nextStep(j);
monkeygoto(-1, i);
nextStep(j);
}
}
}
#include
#include
#include
typedef struct State //描述问题状态
{
int mLeft; //左岸传教士数量
int cLeft; //左岸野人数量
int mRight; //右岸传教士数量
int cRight; //右岸野人数量
int b; //船在左岸还是右岸
};
struct State States[150]; //记录步骤状态
char* routesave[150];
typedef struct Move //描述最优路线
{
char ch[1000]=""; //记录路线
struct Move* Next; //后向结点指针
};
Move *save = new Move; //记录最短的移动方式
int min_move_steps=1000000; //记录最短移动步数
int first = 0; //记录输出第几条路线
int second = 0; //记录输出第几条最优路线
int n1; //传教士人数
int n2; //野人人数
int c; //船的最大容量
void nextStep(int i); /*下一步,递归*/
void showOptimal(); /*输出最优路线*/
void showSolution(int num); /*打印所有路线*/
void Empty(Move *point); /*清空指针链表*/
int Islegal(int num); /*判断当前状态是否越界或不安全*/
int main()
{
//初始化状态
printf("Please input n : ");
scanf_s("%d", &n1);
n2 = n1;
printf("Please input c : ");
scanf_s("%d", &c);
save->Next = NULL;
States[0].mLeft = n1;
States[0].cLeft = n2;
States[0].mRight = 0;
States[0].cRight = 0;
States[0].b = 1;
int a = Islegal(0); //判断输入是否合法
if (a)
{
printf("%s \n", "All Procedure:");
nextStep(0);
}
else printf("输入不合法 \n");
printf("%s \n", "Optimal Procedure:");
showOptimal();
}
/*判断当前状态是否越界或不安全*/
int Islegal(int num)
{
if ((States[num].cLeft > n1) || (States[num].cLeft < 0))
return 0;
if ((States[num].mLeft > n2) || (States[num].mLeft < 0))
return 0;
if ((States[num].mLeft != 0) && (States[num].cLeft > States[num].mLeft))
return 0;
if ((States[num].cRight > n1) || (States[num].cRight < 0))
return 0;
if ((States[num].mRight > n2) || (States[num].mRight < 0))
return 0;
if ((States[num].mRight != 0) && (States[num].cRight > States[num].mRight))
return 0;
return 1;
}
/*清空指针链表*/
void Empty(Move *point)
{
if (point->Next)
Empty(point->Next);
if (point == save)
{
strcpy_s(point->ch, "");
point->Next = NULL;
return;
}
delete point;
}
/*打印路线*/
void showSolution(int num)
{
int c;
if (num < min_move_steps || num == min_move_steps)
{
if (num < min_move_steps)
{
Empty(save);
min_move_steps = num;
}
Move *point = save;
while (point->Next!=NULL)
{
point = point->Next;
}
for (c = 0; c < num; c++)
{
char ccc[6];
ccc[0] = 48 + States[c].mLeft;
ccc[1] = 48 + States[c].cLeft;
ccc[2] = 48 + States[c].b;
ccc[3] = '-';
ccc[4] = '>';
ccc[5] = '\0';
strcat_s(point->ch, ccc);
}
strcat_s(point->ch, "000");
//printf("%s", point->ch);
Move *point_2 = new Move;
point_2->Next = NULL;
point->Next = point_2;
}
printf("No.%d Route:", ++first);
for (c = 0; c < num ; c++)
{
printf("%d%d%d", States[c].mLeft, States[c].cLeft, States[c].b);
printf("->");
}
printf("%d%d%d\n", States[num].mLeft, States[num].cLeft, States[num].b);
}
/*输出最优路线*/
void showOptimal()
{
Move *point = save;
if(!point->Next)
{
printf("%s \n", "Successed or Failed ? : Failed");
return;
}
while(point->Next)
{
printf("No.%d Optimal Route:", ++second);
printf("%s\n", point->ch);
point = point->Next;
}
printf("%s \n", "Successed or Failed ? : Successed");
}
/*下一步,递归*/
void nextStep(int i)
{
if (i >= 150)
{
printf("%s \n", "steplength reached 150,have problem ");
return;
}
for (int k = 0; k < i; k++) /*if the current state is same to previous,retrospect*/ //如果有出现重复走法的,那么退出(通过哈希值进行比较)
if (States[i].mLeft == States[k].mLeft && States[i].cLeft == States[k].cLeft && States[i].b == States[k].b)
return;
if ((States[i].mLeft == 0) && (States[i].cLeft == 0) && (States[i].b == 0))
{
showSolution(i);
return;
}
int next = i + 1;
for (int k = 0; k <= c; k++)
for (int j = 0; j <= c - k; j++)
{
if (k == 0 && j == 0)continue;
if (States[i].b == 1) //由父结点进行状态转移,生成子结点
{
States[next].mLeft = States[i].mLeft - k;
States[next].cLeft = States[i].cLeft - j;
States[next].mRight = States[i].mRight + k;
States[next].cRight = States[i].cRight + j;
States[next].b = 0;
}
else
{
States[next].mLeft = States[i].mLeft + k;
States[next].cLeft = States[i].cLeft + j;
States[next].mRight = States[i].mRight - k;
States[next].cRight = States[i].cRight - j;
States[next].b = 1;
}
if (Islegal(next) == true) //子结点不可行,则删除之
{
nextStep(next);
continue;
}
}
}
(1)对于猴子摘香蕉问题根据自己编写代码或者参考代码,用不同的初始状态测试代码,记录每种初始状态下的输出结果,并对每个结果进行解释。
编写代码思路:使用DFS、递归遍历所有可能的情况并打印出来。香蕉对于猴子来说,不知道在哪里,所以对于猴子来说,要把下一步可能的情况都试一下,因为下一步有多种且是有限确定的走法,借助递归算法做出当前位置的判断,给出下一步的走法即可。
① 猴子在A,箱子在C,香蕉在B,不站在箱子上
结果分析:可以看到有4种路径都可以顺利摘到香蕉,最优的方法是第3种,只需4步。
结果分析:可以看到有2种路径都可以顺利摘到香蕉,最优的方法是第2种,只需3步。
结果分析:可以看到有2种路径都可以顺利摘到香蕉,最优的方法是第2种,只需4步。要先跳下箱子,再推动箱子,爬上去摘到香蕉。
结果分析:猴子和箱子不在同一个位置上,不可能站在箱子上,所以这种初始状态不正确。
(2)完善猴子摘香蕉问题参考代码,参考代码中有什么问题?应该如何修改会更好。
① 递归使用错误,通过下图,可以看到代码出现了一些执行两步后,再调用自身,这种情况会导致第一次打印正确,但后面结果会打印错误的路线。这种情况应该直接修改nextstep()函数内的代码,使得递归正常。
② 该代码只考虑了香蕉在B的情况。在判断到达结束时,该代码给出了States[i].banana == 0限制了香蕉的位置,我们可以通过修改0为我们输入的位置,来获得更加多可能输入的情况。
③ 对于某些输入,打印结果错误。我们发现猴子在爬上箱子后,会直接获得香蕉,这种情况显然是不正确的,所以可以把获得香蕉这一步骤移至代码中的判断到达终点这一步骤,当猴子和箱子和香蕉在同一位置上,并且猴子在箱子上时,可获得香蕉。
④ 对于以上错误,我的代码均以全部纠正,并且完善了原代码的一些冗余地方。
(3)传教士(牧师)与野人问题,写出状态表示的数据结构,还有每种解的状态迁移图示。
typedef struct State //描述问题状态
{
int mLeft; //左岸传教士数量
int cLeft; //左岸野人数量
int mRight; //右岸传教士数量
int cRight; //右岸野人数量
int b; //船在左岸还是右岸
};
typedef struct Move //描述最优路线
{
char ch[1000]=""; //记录路线
struct Move* Next; //后向结点指针
};
两个传教士(牧师)与两个野人,小船一次最多载两人状态迁移图示
(4)尝试写出传教士(牧师)与野人问题的程序清单。
void nextStep(int i); /下一步,递归/
void showOptimal(); /输出最优路线/
void showSolution(int num); /打印所有路线/
void Empty(Move *point); /清空指针链表/
int Islegal(int num); /判断当前状态是否越界或不安全/
全部代码见第五部分——实验源代码
(5)实验结果讨论。
两个传教士(牧师)与两个野人,小船一次最多载两人
由于题目要求输出所有可能的路线,并输出最优的路线,所以使用DFS的递归实现无疑是最简单的实现算法,如果只要求最快地输出一种尽可能快的路线,我们可以使用BFS(最佳优先搜索)算法加快效率找到局部最优解。根据f(n)=h(n),h(n)选择左岸上牧师与野人的总数量可以尽可能快的找到局部最优解。
注:也可以修改估价函数变成实现A*算法,使f(n)=g(n)+h(n),保证效率的前提下,寻得全局最优解。
使用的估价函数:
int Heruistic(STATUS *Node) //估价函数,左岸上牧师与野人数量越少,评价越高
{
return (Node->mLeft + Node->cLeft);
}
BFS(最佳优先搜索)代码如下:
#include
#include
typedef struct status //描述问题状态
{
int mLeft; //左岸传教士数量
int cLeft; //左岸野人数量
int mRight; //右岸传教士数量
int cRight; //右岸野人数量
int b; //船在左岸还是右岸
int Hash; //为了快速判断结点是否存在引入的哈希值
int HVal; //启发函数值
struct status *Next; //后向结点指针
struct status *Prev; //前驱结点指针
} STATUS;
STATUS *OpenHead = new STATUS; //创建Open表,保存所有已生成而未考察的结点
STATUS *CloseHead = NULL; //创建Close表,暂为空,保存所有已考察的结点
int Hash(STATUS *Node); //哈希函数
int Heruistic(STATUS *Node); //启发函数
bool isTarget(STATUS *Current); //判断是否满足目标状态
bool isSafe(STATUS *Node); //判断给定状态是否安全
STATUS* AStar(STATUS *Current); //A*算法主函数
void ResultOut(STATUS *Current); //输出结果
STATUS* GetLastNodeOpen(); //取Open表最后一个节点
STATUS* GetLastNodeClose(); //取Close表最后一个节点
void RemoveOpen(STATUS *Node); //从Open表中删除节点
void RemoveClose(STATUS *Node); //从Close表中删除节点
STATUS* isInOpen(STATUS *Node); //判断指定结点是否在Open表中
STATUS* isInClose(STATUS *Node); //判断指定结点是否在Close表中
void InsertOpen(STATUS *Node); //插入节点到Open表中
void InsertClose(STATUS *Node); //插入节点到Close表中
void SwapOpen(); //对Open表按启发函数排序
int n1 = 2; //传教士人数
int n2 = 2; //野人人数
int c = 2; //船的最大容量
int main()
{
printf("Please input n : ");
scanf_s("%d", &n1);
n2 = n1;
printf("Please input c : ");
scanf_s("%d", &c);
STATUS *Current = NULL;
OpenHead->mLeft = n1; //创建初始状态,得到最初的父节点
OpenHead->cLeft = n2;
OpenHead->mRight = 0;
OpenHead->cRight = 0;
OpenHead->b = 1;
OpenHead->Hash = Hash(OpenHead);
OpenHead->HVal = Heruistic(OpenHead);
OpenHead->Next = NULL;
OpenHead->Prev = NULL;
while (OpenHead) //Open表非空
{
Current = GetLastNodeOpen(); //从Open表中得到启发函数最小的结点作为父节点,并从Open表中删除
RemoveOpen(Current);
if (isTarget(Current)) //达到目标状态则输出结果,否则继续A*算法
{
ResultOut(Current);
//getchar();
return 0;
}
else
{
AStar(Current);
}
}
printf("Successed or Failed?: Failed");
}
void ResultOut(STATUS *Current)
{
Current->Next = NULL;
do //回溯法
{
Current->Prev->Next = Current;
Current = Current->Prev;
} while (Current->Prev != NULL);
int i = 0;
printf("Optimal Procedure :");
while (Current != NULL)
{
printf("%d%d%d", Current->mLeft, Current->cLeft, Current->b);
Current = Current->Next;
if (Current == NULL)break;
printf("->");
}
printf("\nSuccessed or Failed?: Successed\n");
}
STATUS* AStar(STATUS *Current) //实现A*算法
{
for (int i = 0; i <= c; i++)
for (int j = 0; j <= c - i; j++)
{
if (i == 0 && j == 0)continue;
STATUS* Child = new STATUS;
STATUS* NodeTmp;
if (Current->b == 1) //由父结点进行状态转移,生成子结点
{
Child->mLeft = Current->mLeft - i;
Child->cLeft = Current->cLeft - j;
Child->mRight = Current->mRight + i;
Child->cRight = Current->cRight + j;
Child->b = 0;
Child->Hash = Hash(Child);
Child->HVal = Heruistic(Child);
Child->Next = NULL;
Child->Prev = Current;
}
else
{
Child->mLeft = Current->mLeft + i;
Child->cLeft = Current->cLeft + j;
Child->mRight = Current->mRight - i;
Child->cRight = Current->cRight - j;
Child->b = 1;
Child->Hash = Hash(Child);
Child->HVal = Heruistic(Child);
Child->Next = NULL;
Child->Prev = Current;
}
if (isSafe(Child) == true) //子结点不可行,则删除之
{
delete Child;
Child = NULL;
continue;
}
if (((NodeTmp = isInOpen(Child)) == NULL) && \
((NodeTmp = isInClose(Child)) == NULL)) //若子结点不在Open表和Close表中
{
//Child->HVal = Heruistic(Child); //求子结点的启发函数,这里多余了,前面已经求了,kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
InsertOpen(Child); //将子结点插入Open表中
}
else if ((NodeTmp = isInOpen(Child)) != NULL) //若子结点在Open表中
{
if (Heruistic(Child) < Heruistic(NodeTmp)) //子结点的启发函数小于Open表中的启发函数
NodeTmp->HVal = Heruistic(Child); //更新OPEN表中的启发函数
Child->Next = NULL; //删除结点,防止内存泄漏
delete Child;
Child = NULL;
}
else //子结点在CLOSE表中
{
NodeTmp = isInClose(Child); //取得该结点
if (Heruistic(Child) < Heruistic(NodeTmp)) //子结点的启发函数小于Close表中的启发函数
{
NodeTmp->HVal = Heruistic(Child); //更新Close表中的启发函数值
InsertOpen(NodeTmp); //将此结点从CLOSE表中移出, 并放入OPEN表中
RemoveClose(NodeTmp);
}
Child->Next = NULL; //删除结点,防止内存泄漏
delete Child;
Child = NULL;
}
}
InsertClose(Current); //将父节点插入CLOSE表中;
SwapOpen(); //根据启发函数重排OPEN表。这样循环中每一步只考虑OPEN表中状态最好的结点
return NULL;
}
STATUS* isInOpen(STATUS *Node) //使用哈希值查找指定的结点,下同
{
STATUS* NodeTmp = OpenHead;
if (OpenHead == NULL)
return NULL;
do
{
if (Hash(NodeTmp) == Hash(Node))
return NodeTmp;
else
NodeTmp = NodeTmp->Next;
} while (NodeTmp != NULL);
return NULL;
}
STATUS* isInClose(STATUS *Node)
{
STATUS* NodeTmp = CloseHead;
if (CloseHead == NULL)
return NULL;
do
{
if (Hash(NodeTmp) == Hash(Node))
return NodeTmp;
else
NodeTmp = NodeTmp->Next;
} while (NodeTmp != NULL);
return NULL;
}
bool isSafe(STATUS *Node) //判断当前状态是否越界或不安全
{
if ((Node->cLeft > n1) || (Node->cLeft < 0))
return true;
if ((Node->mLeft > n2) || (Node->mLeft < 0))
return true;
if ((Node->mLeft != 0) && (Node->cLeft > Node->mLeft))
return true;
if ((Node->cRight > n1) || (Node->cRight < 0))
return true;
if ((Node->mRight > n2) || (Node->mRight < 0))
return true;
if ((Node->mRight != 0) && (Node->cRight > Node->mRight))
return true;
return false;
}
int Hash(STATUS *Node) //把结点转换成 左岸的牧师与野人与船位置的数,加快查找速度
{
return ((Node->mLeft) << 8) | ((Node->cLeft) << 4) | (Node->b);
}
int Heruistic(STATUS *Node) //估价函数,左岸上牧师与野人数量越少,评价越高
{
return (Node->mLeft + Node->cLeft);
}
bool isTarget(STATUS *Current)
{
if ((Current->mLeft == 0) && (Current->cLeft == 0) && (Current->b == 0))
return true;
else
return false;
}
void InsertOpen(STATUS *Node) //直接插入到链表末尾,下同
{
STATUS *NodeTmp;
if (OpenHead == NULL)
{
OpenHead = Node;
return;
}
else
{
NodeTmp = GetLastNodeOpen();
NodeTmp->Next = Node;
Node->Next = NULL;
}
return;
}
void InsertClose(STATUS *Node)
{
STATUS *NodeTmp;
if (CloseHead == NULL)
{
CloseHead = Node;
return;
}
else
{
NodeTmp = GetLastNodeClose();
NodeTmp->Next = Node;
Node->Next = NULL;
}
return;
}
void RemoveOpen(STATUS *Node)
{
STATUS *NodeTmp = NULL;
STATUS *NodeTmp2 = NULL;
NodeTmp = OpenHead;
while (Hash(NodeTmp) != Hash(Node)) //使用哈希值查找指定的结点,下同
{
NodeTmp2 = NodeTmp;
NodeTmp = NodeTmp->Next;
}
if (NodeTmp == OpenHead)
{
OpenHead = NodeTmp->Next;
NodeTmp = OpenHead;
}
else
NodeTmp2->Next = NodeTmp->Next;
}
void RemoveClose(STATUS *Node)
{
STATUS *NodeTmp = NULL;
STATUS *NodeTmp2 = NULL;
NodeTmp = CloseHead;
while (Hash(NodeTmp) != Hash(Node))
{
NodeTmp2 = NodeTmp;
NodeTmp = NodeTmp->Next;
}
if (NodeTmp == CloseHead)
{
OpenHead = NodeTmp->Next;
NodeTmp = CloseHead;
}
else
NodeTmp2->Next = NodeTmp->Next;
}
STATUS* GetLastNodeOpen() //遍历整个链表并返回最后一个元素,下同
{
STATUS* NodeTmp = OpenHead;
if (NodeTmp == NULL)
return NULL;
else
{
while (NodeTmp->Next != NULL)
{
NodeTmp = NodeTmp->Next;
}
return NodeTmp;
}
}
STATUS* GetLastNodeClose()
{
STATUS* NodeTmp = CloseHead;
if (NodeTmp == NULL)
return NULL;
else
{
while (NodeTmp->Next != NULL)
{
NodeTmp = NodeTmp->Next;
}
return NodeTmp;
}
}
void SwapOpen() //从大到小,直接插入排序
{
STATUS *NodeFirst = NULL;
STATUS *NodeTmp = NULL;
STATUS *Nodeq = NULL;
STATUS *Nodep = NULL;
if (OpenHead == NULL)
return;
NodeFirst = OpenHead->Next; //原链表剩下用于直接插入排序的节点链表
OpenHead->Next = NULL; //只含有一个节点的链表的有序链表
while (NodeFirst != NULL) //遍历剩下无序的链表
{
for (NodeTmp = NodeFirst, Nodeq = OpenHead; ((Nodeq != NULL) && (Nodeq->HVal > NodeTmp->HVal)); \
Nodep = Nodeq, Nodeq = Nodeq->Next); //无序节点在有序链表中找插入的位置
//找到了插入的位置则退出for循环
NodeFirst = NodeFirst->Next; //无序链表中的节点离开,以便它插入到有序链表中
if (Nodeq == OpenHead) //插在第一个节点之前
{
OpenHead = NodeTmp;
}
else //p是q的前驱
{
Nodep->Next = NodeTmp;
}
NodeTmp->Next = Nodeq; //完成插入动作
}
}