西电人工智能大作业.docx
《西电人工智能大作业.docx》由会员分享,可在线阅读,更多相关《西电人工智能大作业.docx(21页珍藏版)》请在冰豆网上搜索。
西电人工智能大作业
人工智能大作业
学生:
021151**
021151**
时间:
2013年12月4号
一.启发式搜索解决八数码问题
1.实验目的
问题描述:
现有一个3*3的棋盘,其中有0-8一共9个数字,0表示空格,其他的数字可以和0交换位置(只能上下左右移动)。
给定一个初始状态和一个目标状态,找出从初始状态到目标状态的最短路径的问题就称为八数码问题。
例如:
实验问题为
2
8
3
1
0
4
7
6
5
1
2
3
8
0
4
7
6
5
到目标状态:
从初始状态:
要求编程解决这个问题,给出解决这个问题的搜索树以及从初始节点到目标节点的最短路径。
2.实验设备及软件环境
利用计算机编程软件VisualC++6.0,用C语言编程解决该问题。
3.实验方法
(1).算法描述:
①.把初始节点S放到OPEN表中,计算
,并把其值与节点S联系起来。
②.如果OPEN表是个空表,则失败退出,无解。
③.从OPEN表中选择一个
值最小的节点。
结果有几个节点合格,当其中有一个为目标节点时,则选择此目标节点,否则就选择其中任一节点作为节点
。
④.把节点
从OPEN表中移出,并把它放入CLOSED的扩展节点表中。
⑤.如果
是目标节点,则成功退出,求得一个解。
⑥.扩展节点
,生成其全部后继节点。
对于
的每一个后继节点
:
a.计算
。
b.如果
既不在OPEN表中,也不在CLOSED表中,则用估价函数
把它添加入OPEN表。
从
加一指向其父辈节点
的指针,以便一旦找到目标节点时记住一个解答路径。
c.如果
已在OPEN表或CLOSED表上,则比较刚刚对
计算过的
值和前面计算过的该节点在表中的
值。
如果新的
值较小,则
I.以此新值取代旧值。
II.从
指向
,而不是指向它的父辈节点。
III.如果节点
在CLOSED表中,则把它移回OPEN表。
⑦转向②,即GOTO②。
(2).流程图描述:
(3).程序源代码:
#include
#include
structnode{
intnumber[3][3];//用二维数组存放8数码
intW;//W表示与目标状态相比错放的数码数
intDepth;//记录当前节点的深度
structnode*parent;//指向父节点的指针
structnode*next;//指向链表中下一个节点的指针
};
intCounterW(intNumber[3][3])
{//函数说明:
计算当前状态与目标状态的W值
inti,j;
intW=0;
intDesnode[3][3]={1,2,3,8,0,4,7,6,5};
for(i=0;i<3;i++)
for(j=0;j<3;j++)
if(Number[i][j]!
=Desnode[i][j])
W++;
returnW;
}
voidPrintNode(node*A)
{
inti,j;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
printf("%d",A->number[i][j]);
printf("\n");
}
printf("\n");
}
intCheckNode(node*open,node*close,inta[3][3])
{//检验该节点状态是否出现过的子程序
intCheckFlag=0;
intflag1,flag2;
node*p=open;
node*q=close;
while(p!
=NULL)
{
flag1=0;
for(inti=0;i<3;i++)
{
for(intj=0;j<3;j++)
if(a[i][j]==p->number[i][j])
flag1++;
}
if(flag1==9)
break;
else
p=p->next;
}
while(q!
=NULL)
{
flag2=0;
for(inti=0;i<3;i++)
{
for(intj=0;j<3;j++)
if(a[i][j]==q->number[i][j])
flag2++;
}
if(flag2==9)
break;
else
q=q->next;
}
if((flag1==9)||(flag2==9))
CheckFlag=1;//如果出现过,置标志位为1
returnCheckFlag;
}
structnode*FindNextNode(node*Prenode,node*open,node*close)
{//扩展Prenode指向的节点,并将扩展所得结点组成一条单链表
inti,j,m,n;//循环变量
inttemp;//临时替换变量
intflag=0;
inta[3][3];//临时存放二维数组
structnode*p,*q,*head;
head=(node*)malloc(sizeof(node));//head指向该链表首结点,并且作为返回值
p=head;
q=head;
head->next=NULL;//初始化
for(i=0;i<3;i++)//找到二维数组中0的位置
{
for(j=0;j<3;j++)
if(Prenode->number[i][j]==0)
{
flag=1;
break;
}
if(flag==1)
break;
}
//根据0的位置的不同,对a进行相应的变换
for(m=0;m<3;m++)//将Prenode->number赋给a
for(n=0;n<3;n++)
a[m][n]=Prenode->number[m][n];
if(i+1<=2)//情况1,0向下移
{
temp=a[i][j];a[i][j]=a[i+1][j];a[i+1][j]=temp;
intCheckNum=CheckNode(open,close,a);
if(CheckNum==0)//若该结点未出现过则执行下面的操作
{
q=(node*)malloc(sizeof(node));
for(m=0;m<3;m++)//将a赋给扩展节点q->number
for(n=0;n<3;n++)
q->number[m][n]=a[m][n];
PrintNode(q);
q->parent=Prenode;
q->Depth=q->parent->Depth+1;//子结点的深度等于其父结点深度加1
q->W=CounterW(q->number);
q->next=NULL;
p->next=q;//扩展节点插入head链表
p=p->next;
}
}
for(m=0;m<3;m++)//将Prenode->number重新赋给a
for(n=0;n<3;n++)
a[m][n]=Prenode->number[m][n];
if(i-1>=0)//情况2,0向上移
{
temp=a[i][j];a[i][j]=a[i-1][j];a[i-1][j]=temp;
intCheckNum=CheckNode(open,close,a);
if(CheckNum==0)//若该结点未出现过则执行下面的操作
{
q=(node*)malloc(sizeof(node));
for(m=0;m<3;m++)//将a赋给q->number
for(n=0;n<3;n++)
q->number[m][n]=a[m][n];
PrintNode(q);
q->parent=Prenode;
q->Depth=q->parent->Depth+1;
q->W=CounterW(q->number);
q->next=NULL;
p->next=q;
p=p->next;
}
}
for(m=0;m<3;m++)
for(n=0;n<3;n++)
a[m][n]=Prenode->number[m][n];
if(j-1>=0)//情况3,0向左移
{
temp=a[i][j];a[i][j]=a[i][j-1];a[i][j-1]=temp;
intCheckNum=CheckNode(open,close,a);
if(CheckNum==0)//若该结点未出现过则执行下面的操作
{
q=(node*)malloc(sizeof(node));
for(m=0;m<3;m++)//将a赋给q->number
for(n=0;n<3;n++)
q->number[m][n]=a[m][n];
PrintNode(q);
q->parent=Prenode;
q->Depth=q->parent->Depth+1;
q->W=CounterW(q->number);
q->next=NULL;
p->next=q;
p=p->next;
}
}
for(m=0;m<3;m++)
for(n=0;n<3;n++)
a[m][n]=Prenode->number[m][n];
if(j+1<=2)//情况4,0向右移
{
temp=a[i][j];a[i][j]=a[i][j+1];a[i][j+1]=temp;
intCheckNum=CheckNode(open,close,a);
if(CheckNum==0)//若该结点未出现过则执行下面的操作
{
q=(node*)malloc(sizeof(node));
for(m=0;m<3;m++)//将a赋给q->number
for(n=0;n<3;n++)
q->number[m][n]=a[m][n];
PrintNode(q);
q->parent=Prenode;
q->Depth=q->parent->Depth+1;
q->W=CounterW(q->number);
q->next=NULL;
p->next=q;
p=p->next;
}
}
head=head->next;
returnhead;
}
node*insert(node*open,node*head)
{//将head链表的结点依次插入到open链表相应的位置,
//使open表中的结点按从小到大排序。
函数返回open指针
node*p,*q;//p、q均指向open表中的结点,p指向q所指的前一个结点
intflag=0;
if((open==NULL)&&(head!
=NULL))//初始状态,open表为空
{//首先将head表第一个结点直接放入open表中
open=head;
q=head;
head=head->next;
q->next=NULL;
}
if((open!
=NULL)&&(open->next==NULL)&&(head!
=NULL))
{//open表中只有一个元素
q=open;
if((head->W+head->Depth)<(open->W+open->Depth))
//若后一结点的f值小于首结点,则将它插入到首结点位置
{
open=head;
head=head->next;
open->next=q;
}
else//或者第二个结点的位置
{
q->next=head;
head=head->next;
q=q->next;
q->next=NULL;
}
p=open;
}
while(head!
=NULL)
{
q=open;
if((head->W+head->Depth)<(open->W+open->Depth))
{//插入到表头
open=head;
head=head->next;
open->next=q;
continue;
}
else
{
q=q->next;p=open;
}//否则,q指像第二个结点,p指向q前一个结点
while(q->next!
=NULL)//将head的一个结点插入到链表中(非表尾的位置)
{
if(q->WW)
{
q=q->next;
p=p->next;
}
else
{
p->next=head;
head=head->next;
p->next->next=q;
break;
}
}
if(q->next==NULL)//将head的一个结点插入到表尾或倒数第二个位置
{
if((q->W+q->Depth)<(head->W+head->Depth))
{
q->next=head;
head=head->next;
q->next->next=NULL;
}
else
{
p->next=head;
head=head->next;
p->next->next=q;
}
}
}
returnopen;
}
voidmain()
{
inti,j;
intkey=0;
nodeFirstNode;
node*open,*close;
node*p,*r;
node*NodeList;
printf("请输入初始状态的8数码(按每行从左往右依次输入,用0表示空格):
\n");
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%d",&FirstNode.number[i][j]);
printf("\n");
printf("搜索树为:
\n");
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
printf("%d",FirstNode.number[i][j]);
printf("\n");
}
printf("\n");
FirstNode.parent=(node*)malloc(sizeof(node));
FirstNode.Depth=0;//开始结点深度为零
FirstNode.W=CounterW(FirstNode.number);
open=&FirstNode;
p=&FirstNode;
if(open->W==0)//该结点的状态与目标结点相同
{
printf("该结点已为目标结点状态!
\n");
return;
}
r=&FirstNode;
close=&FirstNode;
r->next=NULL;
open=NULL;
NodeList=FindNextNode(r,open,close);//NodeList指向新扩展出来的链表
open=insert(open,NodeList);//将扩展出来的结点插入到open表中
while(open!
=NULL)
{
r->next=open;//将open表的第一个元素加到close表,r始终指向close表的最后一个元素
open=open->next;
r=r->next;
r->next=NULL;
if(r->W==0)
{
key=1;
printf("\n启发式搜索成功!
\n");
break;
}
NodeList=FindNextNode(r,open,close);;//对close表最后一个结点进行扩展,扩展得到的链表接到head表
open=insert(open,NodeList);//将扩展的结点按顺序插入到open表中
}
if(key==1)
{
p=close;
printf("最优搜索过程如下:
\n");
printf("结点深度为:
%d\n",FirstNode.Depth);
printf("-------------\n");
while(p!
=NULL)
{
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
printf("%d",p->number[i][j]);
printf("\n");
}
printf("\n");
p=p->next;
if(p!
=NULL)
printf("结点深度为:
%d\n",p->Depth);
printf("-------------\n");
}
}else
printf("查找失败,该问题无解!
\n");
}
4.实验结果
搜索树为:
最短路径为:
5.实验分析
本次实验采用启发式搜索,利用C语言编写程序实现八数码问题的求解。
采用简单的估价函数:
其中
是搜索树中节点的
深度;
用来计算对应于节点
的数据库中错放的棋子个数。
例如初始节点的估价函数值为3。
估价函数能够提供一个评定候选扩展节点的方法,以便确定哪个节点时最有可能在通向目标的最佳路径上。
这样选择正确的估价函数,可以减少扩展节点个数,对于实验所给的初始节点和估价函数,只用扩展11个节点就能找到目标节点;相比于盲目搜索可以减少被扩展的节点数,减少很多空间占用和时间损耗。
利用启发信息来决定哪一个是下一步要扩展的节点,这种搜索总是选择“最有希望”的节点作为下一步被扩展的节点。
这实际上是通过估价函数值的大小重排OPEN表,小的一直排在OPEN表的前面。
每次取OPEN表的第一个节点放到CLOSED表中进行扩展,这样一步步靠近目标节点,直到最后找到目标节点,停止扩展。
6.结论
正确选择估价函数对确定搜索结果具有决定性作用。
使用不能识别某些节点真是希望的估价函数会形成非最小代价路径;而使用一个过多地估计了全部节点希望的估价函数,又会扩展过多的节点。
在选对估价函数的前提下,启发式搜索能准确识别有希望和没有希望的节点,从而很快扩展到目标节点,找到最短路径。
在本次实验中如果选择估价函数值为
(
用来计算对应于节点
的数据库中错放的棋子个数),将会扩展更少的节点,更加快的找到到达目标节点的最短路径。
二.英文期刊翻译
AdaptiveEvolutionaryArtificialNeural
NetworksforPatternClassification
自适应进化人工神经网络模式分类
摘要:
这篇文章提出了一种新的进化方法称为混合进化人工神经网络(HEANN),同时提出进化人工神经网络(ANNs)拓扑结构和权重。
进化算法(EAs)具有较强的全局搜索能力且很可能指向最有前途的领域。
然而,在搜索空间局部微调时,他们效率较低。
HEANN强调全局搜索的平衡和局部搜索的进化过程,通过调整变异概率和步长扰动的权值。
这是区别于大多数以前的研究,那些研究整合EA来搜索网络拓扑和梯度学习来进行权值更新。
四个基准函数被用来测试的HEANN进化框架。
此外,HEANN测试了七个分类基准问题的UCI机器学习库。
实验结果表明在少数几代算法中,HEANN在微调网络复杂性的性能是优越的。
同时,他还保留了相对于其他算法的泛化性能。
简介:
人工神经网络(ANNs)已经成为一种强大的工具被用于模式分类[1],[2]。
ANN拓扑优化和连接权重训练经常被单独处理。
这样一个分治算法产生一个不精确的评价选择的神经网络拓扑结构。
事实上,这两个任务都是相互依存的且应当同时解决以达到最佳结果。
一个模式分类的关键任务是设计一个紧凑和广义ANN拓扑。
为特定的问题选择一个适当的ANN拓扑是至关重要的,由于ANN泛化相关性信息处理能力和ANN拓扑的强关联能力。
过度的小型网络的大小表明问题不能学得很好,而一个特别大的网络规模将导致过度学习和差的推广性能。
耗时的实验训练方法和爬山建设性或修剪算法[3]-[7]用于设计一个ANN架构,对于一个给定的任务只有探索小型建筑子集,或往往是停在结构局部最优解。
相关的神经网络是一个流行的[8]建设性算法而用于构造有多个维层的ANN拓扑。
新的隐藏节点一个接一个的被添加进来且都与每一个现有的隐藏节点在当前的网络连接。
因此,网络可以被视为拥有多个可以形成一个级联结构的集中度值层。
然而,网络是倾向于结构局部最优解由于它具有建设性的行为。
设计一个ANN拓扑使用进化算法(EAs)已经成为一种流行的方法来克服建设性或修剪方法[9]-[13]的缺点。
它有很强的全局搜索能力,可以有效地搜索通过接近完整的ANN拓扑类。
许多工作已经致力于进化神经网络ANN拓扑结构。
两个主要的方法来发展ANN拓扑的文献报告是没有重权值和ANN拓扑同步进化的两个拓扑和权重。
演化的一个无权值得ANN拓扑,必须从一组随机的初始权重训练评估其适应性。
姚和刘[11]指出,这个适应性评价方法很嘈杂因为一个显性型的适应性是用来代表隐形型的适应性。
虽然的进化ANN拓扑的适应性可以通过运行多个不同的随机初始权重而得到的平均结果来估计,为适应性计算评价时间是急剧增加。
因此,只有小ANN拓扑在[14]和[15]中被演化。
同时进化的ANN拓扑和权重,权重信息拓扑和编码在每个个体是独立的。
因此嘈杂的适应性评价问题可以被缓解。
一个突出的工作称为EPNet被姚和刘[11]引入,那是一个同时进化ANN拓扑和权重例子。
姚和刘用混合训练来进化ANN权重测试。
梯度学习和模拟退火都纳入进化过程演变的ANN权重。
Martínez-Estudillo等人在最好的独立