1、八数码的几种解决方法八数码的几种解决帖子关于是八数码的,是前段时间写的,为了给别人一些搜索的样例。现在,事差不多完了,代码还留着,觉着放着浪费,又想自己水平过于欠缺,再加上看在C吧的初学者还是占主要,就觉得发在此处共他们学习还是不错的,仅此而已。 如果有不知道八数码的问题的,可以上网一搜,就什么都明白了。在这里我只用了几种算法实现,其实还有其他方法,我并没有写(如果有人需要,可能我会重新完成)。 首先分析下八数码的一些算法吧。 1:深度优先(DFS): 这个方法起始对八数码非常不好,问题在于搜索过于盲目,搜索深度如果没有限制,有些是根本没必要的(因为八数码的解,大部分集中在2030步之间)。而
2、且即使搜索到一个解,很可能不是最优解。所以用DFS解决这个问题并不明智,不过为了显示其不好,我仍然实现,不过在程序中设置最大深度限制为100。 2:宽度优先搜索(BFS) 宽度优先搜索,解决的还不错,经过对hash函数的优化,能使一个2030步的解在200300MS内解决(不过其还可以做进一步的优化)。 3:迭代加深搜索 迭代加深搜索,可以说是吸取了宽搜和深搜的优点,同时避免了深搜搜到第一个解的不最优性,和宽搜的使用较大的内存空间。 在这里我也用迭代加深搜索,仔细一看的人就发现,起使他只是在深搜上稍微做点“手脚”就完毕,但是其思想,虽然显而易见,可我觉得并不容易想到(指当初提出这个算法的时候)
3、。 4:爬山法 爬山法,经常在人工只能里面见得到,由于其高效,实现简单,所以其有时也是很有用的。但是爬山法,也有缺点,就是搜到的解只是局部最优解,而且有陷入“无解”的风险。爬山法,顾名思意就是一直沿着“最好的方向走”直到“山顶”,就找到解。爬山法,不像回溯,在遍历节点时不保存其他非当前最优节点,也就是说,一旦走错了,就不能再后头搜索其他方向。 所以在我写的爬山法中,对于一个解,虽然很多100多步,但是所需的时间确实0MS左右。 5:双向搜索 双向搜索,前后来搜,碰到头,然后一拼接就出了路径。其搜索的节点数要明显必宽搜少许多,所以在程序中,我并未对其做任何优化,他搜索一个解的时间基本上是0MS。
4、 6:A*算法 A*算法,不用说,解决这个问题很简单。速度也快,都是0MS。具体的可以参考网上的解释。 7:DIA算法 DIA算法可以说是A*与迭代搜索的结合,思想来源于迭代搜索,其实简单的说就是,迭代搜索添加了估价函数。由于时间,当初我并未用该方法实现。第一个贴的代码是宽搜,由于在hash上做了优化,所以对hash部分做下解释: * 对于一个状态,转换为123405678这样的序列,然后分别对应权值: 0:8!,1:7!2:6!,.,8:0! 然后用每个数的逆序数乘上权值的和即为改状态的hash表对应的下标。 对于上序列对应的hash下标为:4*8!+0*7!+0*6!+0*5!+0*4!+
5、0*3!+0*2!+0*1!+0*0!=161280 - 另外对于这种hash值计算外,还有一个由上一个状态来推到下一个状态的hash值的规律: 假设,移动是有从空格的移动方向来看,并且设空格在序列中的位置为i,并且是index为上一个状态的hash值,则下一个状态的hash值为: 上移:index-3*8!+ai-3!*(ai-1ai-3+ai-2ai-3)-ai-2!*(ai-2ai-3)-ai-1!*(ai-1ai+3+ai+2ai+3)+ai+2!*(ai+2ai+3)+ai+1!*(ai+1pi-3)?(Factorialpi-3):(-Factorialpi-2); newinde
6、x+=(pi-1pi-3)?(Factorialpi-3):(-Factorialpi-1); 下移: newindex+=3*40320; newindex-=(pi+2pi+3)?(Factorialpi+3):(-Factorialpi+2); newindex-=(pi+1pi+3)?(Factorialpi+3):(-Factorialpi+1); 因此,对于此种hash值的计算,最多只需要3次加减法,2次比较(平均约2次加减法,3.5次比较运算)运算即可完成hash值的计算,而且这种计算出来的hash值不存在冲突。. *宽搜代码如下: #include #include #incl
7、ude #include #include usingnamespacestd; #defineHashTableSize362880 #defineNOT! typedefstructmaps intdetail33; intparent;/记录父节点在hash表中的位置 intmyindex;/记录自己节点在hash表中的位置 intx,y;/记录空格(0)的坐标 Map,*PMap; Maporg;/初始状态 Mapend;/目标状态 PMapHashTableHashTableSize=NULL;/hash表 charRpath150;/最终的路径 intconstderection4
8、2=-1,0,1,0,0,-1,0,1;/可移动的四个方向 boolFlageNew; /* * *八数码的输入(在这里不做任何输入检查,均认为输入数据是正确的) * */ voidinput() inti,j; intsum; for(i=0;i9;i+) scanf(%1d,*org.detail+i); if(0=*(*org.detail+i) org.x=i/3; org.y=i%3; org.parent=-1; for(i=0;i9;i+)/计算逆序 if(0=*(*org.detail+i) continue; for(j=0;ji;j+) if(0!=*(*org.detai
9、l+j)&*(*org.detail+j)*(*org.detail+i) sum+; if(sum%2=0)/目标状态各个数字对应的坐标位置 end.detail00=1,end.detail01=2,end.detail02=3; end.detail10=4,end.detail11=5,end.detail12=6; end.detail20=7,end.detail21=8,end.detail22=0; else end.detail00=1,end.detail01=2,end.detail02=3; end.detail10=8,end.detail11=0,end.detai
10、l12=4; end.detail20=7,end.detail21=6,end.detail22=5; return; /* * *检测两个状态是否一样 * */ inlineboolIsEqual(Mapa,Mapb) return0=memcmp(constvoid*)(*a.detail),(constvoid*)(*b.detail),36); /* * *hash值的计算 * */ intHashValue(Mapa) intcount; inti,j; intvalue=0; staticintpv9=1,1,2,6,24,120,720,5040,40320; for(i=0;
11、i9;i+) for(j=0,count=0;ji;j+) if(*(*a.detail+i)*(*a.detail+j) count+; value+=pvi*count; returnvalue; /* * *状态插入到hash表中。返回的是插入到的hash表中对应的下标值 * */ intInsertHashTable(Mapa,intparent) intindex=HashValue(a); if(NULL=HashTableindex)/如果为TURE,那么说明hash表中不存在该状态,应该插入到hash表中 a.parent=parent; HashTableindex=newM
12、ap; *HashTableindex=a; FlageNew=true; else FlageNew=false; returnindex; /* * *宽度优先搜索 * */ voidBfs() queueQueue; boolFinish=false; org.myindex=InsertHashTable(org,-1); Queue.push(org); while(false=Queue.empty()&NOTFinish) Mapnode=Queue.front(); Queue.pop(); /printf(%dn,Queue.size(); if(true=IsEqual(n
13、ode,end) Finish=true; end.parent=node.parent; end.myindex=node.myindex; continue; for(intk=0;k4;k+) Maptmp=node; tmp.x=node.x+derectionk0, tmp.y=node.y+derectionk1; if(tmp.x2|tmp.y2) continue; tmp.detailnode.xnode.y=tmp.detailtmp.xtmp.y;/移动空格 tmp.detailtmp.xtmp.y=0; inttmpindex=InsertHashTable(tmp,n
14、ode.myindex); if(false=FlageNew)/如果不是新状态的,即以前访问过改节点,不再加入队列中 continue; tmp.parent=node.myindex; tmp.myindex=tmpindex; Queue.push(tmp); return; /* * *通过hash表中记录的进行查找路径 * */ voidFindPath() Mapnow; stackStack; Stack.push(end); now=end; intcount=0; while(now.parent!=-1) Stack.push(*HashTablenow.parent);
15、now=Stack.top(); printf(共需:%d步n,Stack.size()-1); getchar(); while(NOTStack.empty() now=Stack.top(); Stack.pop(); for(inti=0;i3;i+) for(intj=0;j3;j+) printf(%3d,now.detailij); printf(n); if(0!=Stack.size() printf(n第%d步n,+count); getchar(); printf(nTheEnd!n); return; intmain() input(); longtime=GetTic
16、kCount(); Bfs(); printf(计算用时:%dMSn,GetTickCount()-time); FindPath(); return0; 接下来的第二个代码是: 深度优先搜索(限制了他的深度为50) #include #include #include #include #include usingnamespacestd; #defineHashTableSize362880 #defineNOT! #defineMaxDeep50 typedefstructmaps intdetail33; intx,y;/记录空格(0)的坐标 Map,*PMap; Maporg;/初始
17、状态 Mapend;/目标状态 boolHashTableHashTableSize=false;/hash表 intconstderection42=-1,0,1,0,0,-1,0,1;/可移动的四个方向 intPathMaxDeep+1; intStep; boolFinish; /* * *八数码的输入(在这里不做任何输入检查,均认为输入数据是正确的) * */ voidinput() inti,j; intsum; for(i=0;i9;i+) scanf(%1d,*org.detail+i); if(0=*(*org.detail+i) org.x=i/3; org.y=i%3; f
18、or(i=0;i9;i+)/计算逆序 if(0=*(*org.detail+i) continue; for(j=0;ji;j+) if(0!=*(*org.detail+j)&*(*org.detail+j)*(*org.detail+i) sum+; if(sum%2=0)/目标状态各个数字对应的坐标位置 end.detail00=1,end.detail01=2,end.detail02=3; end.detail10=4,end.detail11=5,end.detail12=6; end.detail20=7,end.detail21=8,end.detail22=0; else end.detail00=1,end.detail01=2,end.detail02=3; end.detail10=8,end.detail11=0,end.detail12=4; end.detail20=7,end.detail21=6,end.detail22=5; return; /* * *检测两个状态是否一样 * */ inlineboolIsEqual(Mapa,Mapb) return0=memcmp(constvoid*)(*a.detail),(constvoid*)(*b.detail),36); /*
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1