A算法人工智能课程设计.docx
《A算法人工智能课程设计.docx》由会员分享,可在线阅读,更多相关《A算法人工智能课程设计.docx(12页珍藏版)》请在冰豆网上搜索。
A算法人工智能课程设计
人工智能(A*算法)
一、A*算法概述
A*算法是到目前为止最快的一种计算最短路径的算法,但它一种‘较优’算法,即它一般只能找到较优解,而非最优解,但由于其高效性,使其在实时系统、人工智能等方面应用极其广泛。
A*算法结合了启发式方法(这种方法通过充分利用图给出的信息来动态地作出决定而使搜索次数大大降低)和形式化方法(这种方法不利用图给出的信息,而仅通过数学的形式分析,如Dijkstra算法)。
它通过一个估价函数(HeuristicFunction)f(h)来估计图中的当前点p到终点的距离(带权值),并由此决定它的搜索方向,当这条路径失败时,它会尝试其它路径。
因而我们可以发现,A*算法成功与否的关键在于估价函数的正确选择,从理论上说,一个完全正确的估价函数是可以非常迅速地得到问题的正确解答,但一般完全正确的估价函数是得不到的,因而A*算法不能保证它每次都得到正确解答。
一个不理想的估价函数可能会使它工作得很慢,甚至会给出错误的解答。
为了提高解答的正确性,我们可以适当地降低估价函数的值,从而使之进行更多的搜索,但这是以降低它的速度为代价的,因而我们可以根据实际对解答的速度和正确性的要求而设计出不同的方案,使之更具弹性。
二、A*算法分析
众所周知,对图的表示可以采用数组或链表,而且这些表示法也各也优缺点,数组可以方便地实现对其中某个元素的存取,但插入和删除操作却很困难,而链表则利于插入和删除,但对某个特定元素的定位却需借助于搜索。
而A*算法则需要快速插入和删除所求得的最优值以及可以对当前结点以下结点的操作,因而数组或链表都显得太通用了,用来实现A*算法会使速度有所降低。
要实现这些,可以通过二分树、跳转表等数据结构来实现,我采用的是简单而高效的带优先权的堆栈,经实验表明,一个1000个结点的图,插入而且移动一个排序的链表平均需500次比较和2次移动;未排序的链表平均需1000次比较和2次移动;而堆仅需10次比较和10次移动。
需要指出的是,当结点数n大于10,000时,堆将不再是正确的选择,但这足已满足我们一般的要求。
求出2D的迷宫中起始点S到目标点E的最短路径?
算法:
findpath()
{
把S点加入树根(各点所在的树的高度表示从S点到该点所走过的步数);
把S点加入排序队列(按该点到E点的距离排序+走过的步数从小到大排序);
1、排序队列sort_queue中距离最小的第一个点出列,并保存入store_queue中
2、从出列的点出发,分别向4个(或8个)方向中的一个各走出一步
3、并估算第2步所走到位置到目标点的距离,并把该位置加入树,最后把该点按距离从小到大排序后并放入队列中(由trytile函数实现)
4、如果该点从四个方向上都不能移动,则把该点从store_queue中删除
5、回到第一点,直到找到E点则结束
从目标点回溯树,直到树根则可以找到最佳路径,并保存在path[]中
}
文末附带的程序参考了风云的最短路径代码,并加以改进和优化:
把原来用于存放已处理节点的堆栈改为队列(store_queue),这样在从sort_queue队列出列时可直接放入store_queue中。
解除了地图大小的限制(如果有64K内存限制时,地图大小只能是180x180)。
删除了原程序中的一些冗余,见程序中的注释。
程序继续使用dis_map数组保存各点历史历史最佳距离,也包含了某点是否已经经过的信息,虽然这样做可能会比使用链表多用一些内存,但是在搜索时可以节省不时间。
程序更具有实用性,可直接或修改后运用于你的程序中,但请你使用该代码后应该返回一些信息给我,如算法的改进或使用于什么程序等。
三、A*算法程序
本程序可以用BorlandC++或DJGPP编译
#include
#include
#include
#include
#definetile_num(x,y)((y)*map_w+(x))//将x,y坐标转换为地图上块的编号
#definetile_x(n)((n)%map_w)//由块编号得出x,y坐标
#definetile_y(n)((n)/map_w)
#defineMAPMAXSIZE180//地图面积最大为180x180,如果没有64K内存限制可以更大
#defineMAXINT32767
//树结构,比较特殊,是从叶节点向根节点反向链接,方便从叶节点找到根节点
typedefstructtree_node*TREE;
structtree_node{
inth;//节点所在的高度,表示从起始点到该节点所有的步数
inttile;//该节点的位置
TREEfather;//该节点的上一步
};
//链接结构,用于保存处理过的和没有处理过的结点
typedefstructlink_node*LINK;
structlink_node{
TREEnode;
intf;
LINKnext;
};
LINKsort_queue;//保存没有处理的行走方法的节点
LINKstore_queue;//保存已经处理过的节点(搜索完后释放)
unsignedchar*map;//地图数据
unsignedint*dis_map;//保存搜索路径时,中间目标地最优解
intmap_w,map_h;//地图宽和高
intstart_x,start_y,end_x,end_y;//地点,终点坐标
//初始化队列
voidinit_queue()
{
sort_queue=(LINK)malloc(sizeof(*sort_queue));
sort_queue->node=NULL;
sort_queue->f=-1;
sort_queue->next=(LINK)malloc(sizeof(*sort_queue));
sort_queue->next->node=NULL;
sort_queue->next->f=MAXINT;
sort_queue->next->next=NULL;
store_queue=(LINK)malloc(sizeof(*store_queue));
store_queue->node=NULL;
store_queue->f=-1;
store_queue->next=NULL;
}
//待处理节点入队列,依靠对目的地估价距离插入排序
voidenter_queue(TREEnode,intf)
{
LINKp=sort_queue,father,q;
while(f>p->f){
father=p;
p=p->next;
assert(p);
}
q=(LINK)malloc(sizeof(*q));
assert(sort_queue);
q->f=f,q->node=node,q->next=p;
father->next=q;
}
//将离目的地估计最近的方案出队列
TREEget_from_queue()
{
LINKbestchoice=sort_queue->next;
LINKnext=sort_queue->next->next;
sort_queue->next=next;
bestchoice->next=store_queue->next;
store_queue->next=bestchoice;
returnbestchoice->node;
}
//释放栈顶节点
voidpop_stack()
{
LINKs=store_queue->next;
assert(s);
store_queue->next=store_queue->next->next;
free(s->node);
free(s);
}
//释放申请过的所有节点
voidfreetree()
{
inti;
LINKp;
while(store_queue){
p=store_queue;
free(p->node);
store_queue=store_queue->next;
free(p);
}
while(sort_queue){
p=sort_queue;
free(p->node);
sort_queue=sort_queue->next;
free(p);
}
}
//估价函数,估价x,y到目的地的距离,估计值必须保证比实际值小
intjudge(intx,inty)
{
intdistance;
distance=abs(end_x-x)+abs(end_y-y);
returndistance;
}
//尝试下一步移动到x,y可行否
inttrytile(intx,inty,TREEfather)
{
TREEp=father;
inth;
if(map[tile_num(x,y)]!
='')return1;//如果(x,y)处是障碍,失败
h=father->h+1;
if(h>=dis_map[tile_num(x,y)])return1;//如果曾经有更好的方案移动到(x,y)失败
dis_map[tile_num(x,y)]=h;//记录这次到(x,y)的距离为历史最佳距离
//将这步方案记入待处理队列
p=(TREE)malloc(sizeof(*p));
p->father=father;
p->h=father->h+1;
p->tile=tile_num(x,y);
enter_queue(p,p->h+judge(x,y));
return0;
}
//路径寻找主函数
int*findpath(void)
{
TREEroot;
inti,j;
int*path;
memset(dis_map,0xff,map_h*map_w*sizeof(*dis_map));
init_queue();
root=(TREE)malloc(sizeof(*root));
root->tile=tile_num(start_x,start_y);
root->h=0;
root->father=NULL;
enter_queue(root,judge(start_x,start_y));
for(;;){
intx,y,child;
TREEp;
root=get_from_queue();
if(root==NULL){
returnNULL;
}
x=tile_x(root->tile);
y=tile_y(root->tile);
if(x==end_x&&y==end_y)break;//达到目的地成功返回
child=trytile(x,y-1,root);//尝试向上移动
child&=trytile(x,y+1,root);//尝试向下移动
child&=trytile(x-1,y,root);//尝试向左移动
child&=trytile(x+1,y,root);//尝试向右移动
if(child!
=0)
pop_stack();//如果四个方向均不能移动,释放这个死节点
}
path=(int*)malloc((root->h+2)*sizeof(int));
assert(path);
for(i=0;root;i++){
path[i]=root->tile;
root=root->father;
}
path[i]=-1;
freetree();
returnpath;
}
voidprintpath(int*path)
{
inti;
if(path==NULL)return;
for(i=0;path[i]>=0;i++){
gotoxy(tile_x(path[i])+1,tile_y(path[i])+1);
cprintf(".");
}
}
intreadmap()
{
FILE*f;
inti,j;
f=fopen("map.dat","r");
assert(f);
fscanf(f,"%d,%d\n",&map_w,&map_h);
map=malloc(map_w*map_h+1);
assert(map);
for(i=0;ifgets(map+tile_num(0,i),map_w+2,f);
fclose(f);
start_x=-1,end_x=-1;
for(i=0;ifor(j=0;jif(map[tile_num(j,i)]=='s')map[tile_num(j,i)]='',start_x=j,start_y=i;
if(map[tile_num(j,i)]=='e')map[tile_num(j,i)]='',end_x=j,end_y=i;
}
assert(start_x>=0&&end_x>=0);
dis_map=malloc(map_w*map_h*sizeof(*dis_map));
assert(dis_map);
return0;
}
voidshowmap()
{
inti,j;
clrscr();
for(i=0;igotoxy(1,i+1);
for(j=0;jif(map[tile_num(j,i)]!
='')cprintf("O");
elsecprintf("");
}
gotoxy(start_x+1,start_y+1);
cprintf("s");
gotoxy(end_x+1,end_y+1);
cprintf("e");
}
intmain()
{
int*path;
readmap();
showmap();
getch();
path=findpath();
printpath(path);
if(dis_map)free(dis_map);
if(path)free(path);
if(map)free(map);
getch();
return0;
}
--[if!
supportLineBreakNewLine]-->
--[endif]-->
四、运行结果
五、心得
本次大作业自己努力做了前面的分析,虽然程序简单,在一开始运行的时候找不到主函数,经过认真的改正,终于发现了问题,自己的编程技巧不是很好,借鉴了资料和同学们的意见终于可以能够运行,并的出结果,在次人工智能大作业中学到了很多的专业知识,也知道人工只能也是一门很重要的课程。