寻路问题常见于各类游戏中角色寻路、三维虚拟场景中运动目标的路径规划,机器人寻路等多个应用领域。迷宫寻路问题是在以方格表示的地图场景中,对于给定的起点,终点和障碍物(墙),如何找到一条从起点开始避开障碍物到达终点的最短路径。 假设在一个nxm的迷宫里,入口坐标和出口坐标分别为(1,1)和(5,5),每一个坐标点有两种可能:0或1,其中0表示该位置允许通过,1表示该位置不允许通过。迷宫图如下: 以迷宫寻路问题为例实现A*算法的求解程序,要求设计两种不同的估价格函数。 |
先将迷宫数组化:我将其周围全都加了一面墙,即一圈都是1,如下图所示: 然后再来进行后续的操作。 迷宫内的每一个点都用一个结构体来表示,结构体内包含了六个变量:jiedian=struct('node',1,'top',1,'bottom',1,'left',1,'right',1,'length',0); 1代表是墙不能通过,0代表可以通过的路,length是该节点已经走过的路程。 初始化迷宫表: for i=2:6 for j=2:6 %初始化 migongs(i,j).node=migong(i,j); migongs(i,j).top=migong(i-1,j); migongs(i,j).bottom=migong(i+1,j); migongs(i,j).left=migong(i,j-1); migongs(i,j).right=migong(i,j+1); migongs(i,j).length=0; end end 一共七行七列,因为最外层是墙所以不用初始化。 创建一个open表来记录要扩展的节点,一个变量openodenum来记录open表中还剩余的节点,一个变量totlenode来记录所有的增加到open表中的节点数,一个变量expand来记录扩展过的节点数。开始时变量状态如下: open=[2;2]; %表示open表中的第一个节点是迷宫表中的第2行,第2列 openodenum=1; totlenode=1; expand=0; Open表中最开始记录的是start节点,写一个while循环,从start节点开始扩展,每扩展一个节点,expand就+1,如果被扩展节点的周围有可扩展的节点,就把它加入到open表中,并且要记录其走过的路程为上一个节点走过的路程+1。 没有创建close表因为每次扩展一个节点时我都要把他的node变为1,并且其周围节点的相应边也变为1,也就是说被扩展过了不能再扩展了,比每次循环去找close表中的节点要快一点。 扩展完一个节点后,将open表中的所有节点前移,也即删掉第一个扩展完的节点,然后再从open表中选出下一个要扩展的节点,把它的位置换到open表中的第一个位置,此处就需要自己设计一个启发式函数来找到最优的该扩展的节点。 就这样在while中循环,每次扩展一个算法认为是最优的节点来找最优的路,从而完成迷宫寻路问题。 关于估价函数,此处我写了三个: 对于书上的迷宫图:
H=6-open(1,1)+6-open(2,1); %估计剩余的路程 F=G+H 比较已经走过的路程和估计剩余的路程的和。 运行书上的那个较小的迷宫图时,此三种估价函数所寻找出来的结果都差不多,最终如下图所示: 其运行时间也都在0.04-0.1之间,不能体现(3)的优点,然后换一个大的迷宫: 在上图的迷宫进行寻路时,估价函数(3)就显示出了它优点,在用前两个估价函数进行搜索时,会产生局部最优解,不能找到全局最优解,且其产生的节点也比较多,寻路时间比较长,如下图: 第三种估价函数比前两种估价函数要好,不管产生多少节点,它总能够产生最优解,但可能在非常复杂的迷宫中会用较多的时间来寻路,但只要有解,它总能找到解,且是最优解。 其过程如下图所示:
此为第三种估价函数所运行出来的结果 此为第二种估价函数运行出来的结果 |
|
st=cputime; jiedian=struct('node',1,'top',1,'bottom',1,'left',1,'right',1,'length',0); migong=[1,1,1,1,1,1,1,1,1,1,1,1,1; 1,0,0,0,0,0,0,0,1,1,1,0,1; 1,1,0,1,0,1,1,0,1,1,0,0,1; 1,0,0,1,1,1,1,0,0,0,0,0,1; 1,0,1,0,0,0,1,0,1,1,0,0,1; 1,0,1,0,1,0,1,0,1,1,1,0,1; 1,0,1,0,1,0,1,0,0,1,0,0,1; 1,0,1,0,1,0,1,1,0,0,0,1,1; 1,0,1,0,1,0,1,1,0,1,1,0,1; 1,0,1,0,1,0,0,0,0,1,0,0,1; 1,0,1,0,1,1,1,1,1,1,1,0,1; 1,0,0,0,0,0,0,0,0,0,0,0,1; 1,1,1,1,1,1,1,1,1,1,1,1,1] migongs=[jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian; jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian; jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian; jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian; jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian; jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian; jiedian,jiedian,jiedian,jiedian,jiedian,jiedian,jiedian]; open=[2;2]; openodenum=1; totlenode=1; expand=0; for i=2:12 for j=2:12 %初始化 migongs(i,j).node=migong(i,j); migongs(i,j).top=migong(i-1,j); migongs(i,j).bottom=migong(i+1,j); migongs(i,j).left=migong(i,j-1); migongs(i,j).right=migong(i,j+1); migongs(i,j).length=0; end end while(openodenum>0) %扩展节点 migongs(open(1,1),open(2,1)).node=1; disp(['扩展节点:','(',num2str(open(1,1)-1),',',num2str(open(2,1)-1),')']) expand=expand+1; if((open(1,1))==12&&open(2,1)==12) disp('成功!') totlenode expand break; end if(migongs(open(1,1),open(2,1)).top==0) migongs(open(1,1)-2,open(2,1)).bottom=1; migongs(open(1,1)-1,open(2,1)).bottom=1; migongs(open(1,1)-1,open(2,1)-1).right=1; migongs(open(1,1)-1,open(2,1)+1).left=1; migongs(open(1,1)-1,open(2,1)).length=migongs(open(1,1),open(2,1)).length+1; openodenum=openodenum+1; open(1,openodenum)=open(1,1)-1; open(2,openodenum)=open(2,1); totlenode=totlenode+1; x=open(1,1)-1-1; y=open(2,1)-1; disp(['增加节点:','(',num2str(x),',',num2str(y),')']) end if(migongs(open(1,1),open(2,1)).bottom==0) migongs(open(1,1)+2,open(2,1)).top=1; migongs(open(1,1)+1,open(2,1)).top=1; migongs(open(1,1)+1,open(2,1)-1).right=1; migongs(open(1,1)+1,open(2,1)+1).left=1; migongs(open(1,1)+1,open(2,1)).length=migongs(open(1,1),open(2,1)).length+1; openodenum=openodenum+1; open(1,openodenum)=open(1,1)+1; open(2,openodenum)=open(2,1); totlenode=totlenode+1; x=open(1,1)+1-1; y=open(2,1)-1; disp(['增加节点:','(',num2str(x),',',num2str(y),')']) end if(migongs(open(1,1),open(2,1)).left==0) migongs(open(1,1),open(2,1)-2).right=1; migongs(open(1,1),open(2,1)-1).right=1; migongs(open(1,1)-1,open(2,1)-1).bottom=1; migongs(open(1,1)+1,open(2,1)+1).top=1; migongs(open(1,1),open(2,1)-1).length=migongs(open(1,1),open(2,1)).length+1; openodenum=openodenum+1; open(1,openodenum)=open(1,1); open(2,openodenum)=open(2,1)-1; totlenode=totlenode+1; x=open(1,1)-1; y=open(2,1)-1-1; disp(['增加节点:','(',num2str(x),',',num2str(y),')']) end if(migongs(open(1,1),open(2,1)).right==0) migongs(open(1,1),open(2,1)+2).left=1; migongs(open(1,1),open(2,1)+1).left=1; migongs(open(1,1)-1,open(2,1)+1).bottom=1; migongs(open(1,1)+1,open(2,1)+1).top=1; migongs(open(1,1),open(2,1)+1).length=migongs(open(1,1),open(2,1)).length+1; openodenum=openodenum+1; open(1,openodenum)=open(1,1); open(2,openodenum)=open(2,1)+1; totlenode=totlenode+1; x=open(1,1)-1; y=open(2,1)+1-1; disp(['增加节点:','(',num2str(x),',',num2str(y),')']) end for n=1:openodenum-1 open(1,n)=open(1,n+1); open(2,n)=open(2,n+1); end openodenum=openodenum-1; tempi=0; tempj=0; G=migongs((open(1,1)),(open(2,1))).length; %已经走过的路程 F=G+H H=6-open(1,1)+6-open(2,1); %估计剩余的路程 Gx=migongs((open(1,n)),(open(2,n))).length; Hx=6-open(1,n)+6-open(2,n); for n=1:openodenum if(G+H>Gx+Hx) %找出F最小的那个节点继续扩展 tempi=open(1,1); tempj=open(2,1); open(1,1)=open(1,n); open(2,1)=open(2,n); open(1,n)=tempi; open(2,n)=tempj; end end end et=cputime-st |