??xml version="1.0" encoding="utf-8" standalone="yes"?>91久久九九无码成人网站,久久久久一区二区三区,久久久精品国产sm调教网站 http://www.shnenglu.com/zzfmars/category/14312.html行胜于言Q孤行无益,思ؓ行先。有所为,有所不ؓ?/description>zh-cnSat, 21 Aug 2010 13:16:59 GMTSat, 21 Aug 2010 13:16:59 GMT60pku1077http://www.shnenglu.com/zzfmars/articles/124209.htmlKevin_ZhangKevin_ZhangSat, 21 Aug 2010 11:57:00 GMThttp://www.shnenglu.com/zzfmars/articles/124209.htmlhttp://www.shnenglu.com/zzfmars/comments/124209.htmlhttp://www.shnenglu.com/zzfmars/articles/124209.html#Feedback0http://www.shnenglu.com/zzfmars/comments/commentRss/124209.htmlhttp://www.shnenglu.com/zzfmars/services/trackbacks/124209.htmlhttp://blog.163.com/sentimental_man/blog/static/73001618200881405851176/
介:
         所谓八数码问题是指q样一U游戏:分别标有数?Q?Q?Q?#8230;Q?的八块正方Ş数码牌Q意地攑֜一?×3的数码盘上。放牌时要求不能重叠。于是,?×3的数码盘上出C一个空根{现在要求按照每ơ只能将与空格相ȝ数码牌与I格交换的原则,Q意摆攄数码盘逐步摆成某种Ҏ的排列。如下图表示了一个具体的八数码问题求解?br>
问题分析Q?/span>
         首先Q八数码问题包括一个初始状?START) 和目标状?END)Q所谓解八数码问题就是在两个状态间L一pd可过渡状态(START->STATE1->STATE2->...->ENDQ。这个状态是否存在就是我们要解决的第一个问题:

Q1Q每一个状态及每一ơ操作的表示ҎQ?/span>
         有许多表C方法,比如一?*3的八数码盘可以压~成一个intDC,但不适用?5 puzzle或大? 的puzzle问题。如果对I间要求很高Q应该还可以再压~。本文采用一个int表示的方法?br>         表示Ҏ如下Q由于int的表C围大?e9Q所以我们取一个int的低9位,Z方便LI格的位|,int的个位我们用来放I格的位|(1-9Q。而前8位,按照行从上到下,列从左到右的序依次记录对应位置上的数字。例如:
         可以表示?2 3 1 5 8 4 6 7 5 Q个位的5表示I格在第5位,前八位数按顺序记录。坐标{换公式ؓQ?br>     numQ压~后的intQ?x yQ求x行y?1记vQ?e(n)?1?0的n?br>     int temp=(x-1)*3+y
     if   temp > num%10 then return (num / 1e(9-temp+1)) %10
     else return (num / 1e(9-temp) )%10

     Z方便本文介绍Q取目标状态ؓQ? 2 3 4 5 6 7 8 9 ?->

         操作本文?u r d l 分别表示 I格的向?向右 向下向左四个操作。比如,在简介中的图包括两步操作 l d Q可能与qx玩这cL戏的习惯不符合,但这是ؓ了和ACM例题相统一?br>
     对应圎ͼ每种操作引v的状态变化如下:
     r Qnum?+              l Qnum?-
     u Q有点复?br>     int t0 = 9-num%10 + 1
     int t1 = num / 1e(t0)
     int t2 = t1%1000
     t1= t1- t2 + (t2 % 100) * 10 + t2 / 100
     t1*= 1e(t0)
     return (t1 + ( (num % t0) - 3))
     d Qreturn前面同u操作Q?returnq回   (t1 + ( (num % t0) + 3))

Q2Q判断是否存在中间状态START 到达ENDQ?/span>
         用组合数学的Ҏ可以快速地q行判断Q例如SOJ 2061?2360题中都是关于此类的问题。但八数码的判断Ҏ比他们简单多了?br>
         本文用的Ҏ是计排列的逆序数|? 3 1 5 8 4 6 7 5 Z子,5表示的是I格Q不计算Q那么求23158467 的逆序gؓ
         0 + 0 + 2 (1<2 1<3 ) + 0 + 0 + 1 ( 4<5 ) + 1 ( 6<8 ) + 1 ( 7<8 ) = 5
目标状? 2 3 4 5 6 7 8 9 的逆序自然是0?br>
两个状态之间是否可达,可以通过计算两者的逆序|若两者奇偶性相同则可达Q不然两个状态不可达?br>
单证明一下:
         l ?r 操作Q不会媄响状态的逆序|因ؓ只会改变个位敎ͼI格的位|)?br>         u和d操作是某个位置的数??左移两位。由于数字序列的每一ơ移动会佉K序值奇偶性改变,所?nbsp;            Ud两次后奇偶性不变?br>         所以四个操作均不会影响序列的奇偶性?br>
Q3Q如何寻找一pd的中间状态及遇到的问题?
         要寻找这一pd中间状态的Ҏ是搜索,但搜索很Ҏ遇到旉和空间上的问题。以下就是搜索的基本原理Q?br>
         ? 3 7 2 4 6 8 5 2 状态可以衍生三个状态,假如选择? 2 3 7 4 6 8 5 5 Q则又衍生三个状态,l箋按某{略q行选择Q一直到衍生出的新状态ؓ目标状态END 为止?br>Ҏ看出Q这L搜烦cM于从树根开始向茎再向叶搜烦目标叶子一L树型状。由于其规模的不断扩大,其叶子也愈加茂密Q最l的规模会大到无法控制。这样在I间上会大大加大搜烦隑ֺQ在旉上也要消耗许多?br>
在普通搜索中遇到以下问题Q?br>        a   搜烦中易出现循环Q即讉K某一个状态后又来讉K该状态?br>        b 搜烦路径不佳便无法得到较好的中间状态集Q即中间状态集的元素数量过大)?br>        c 搜烦q程中访问了q多的无用状态,q些状态对最后的l果无帮助?br>

         以上三个问题中,a命问题,应该它可能导致程序死循环Qb和c是非致命的,但若不处理好可能D性能急剧下降?br>

Q4Q怎样避免重复讉K一个状态?
         最直接的方法是记录每一个状态访问否Q然后再衍生状态时不衍生那些已l访问的状态了。思想是,l每个状态标C个flagQ若该状态flag = true则不衍生Q若为false则衍生ƈ修改flag为true?br>在某些算法描q里Q称有两个链表,一个ؓz链表(待访问)Q一个ؓ死链表(讉K完)。每一ơ衍生状态时Q先判断它是否已在两个链表中Q若存在Q则不衍生;若不存在Q将其放入活链表。对于被衍生的那个状态,攑օ死链表?br>
         Z记录每一个状态是否被讉Kq,我们需要有_的空间。八数码问题一共有9!Q这个数字ƈ不是很大Q但q面而来的另一个问题是我们如何快速访问这些状态,如果是单U用链表的话Q那么在规模相当大,查找的状态数目十分多的时候就不能快速找到状态,其复杂度为O(n),Z解决q个问题Q本文将采用哈希函数的方法,使复杂度减ؓO(1)?br>         q里的哈希函数是用能对许多全排列问题适用的方法。取n!为基敎ͼ状态第n位的逆序gؓ哈希值第n位数。对于空|取其Q?-位置Q再乘以8!。例如,1 3 7 2 4 6 8 5 8 的哈希值等于:
0*0! + 0*1! + 0*2! + 2*3! + 1*4! + 1*5! + 0*6! + 3*7! + (9-8)*8! = 55596 <9!

         具体的原因可以去查查一些数学书Q其? 2 3 4 5 6 7 8 9 的哈希值是0 最,8 7 6 5 4 3 2 1 0 的哈希值是Q?!-1Q最大,而其他值都? 刎ͼ9!-1Q?中,且均唯一?br>
Q5Q如何搜烦只求得最佳的解?
         普通的搜烦UCؓDFSQ深度优先搜索)。除了DFSQ还有BFSQ从概念上讲Q两者只是在扩展时的方向不同QDFS向深扩张Q而BFS向广扩张。在八数码问题的解集树中Q树的深度就表示了从初始态到目标态的步数QDFS一呛_深,所以很Ҏ扑և深度较大的解?br>         BFS可以保证解的深度最,因ؓ在未同一深度的状态全部访问完前,BFS不会去访问更q状态,因此比较适合八数码问题,臛_能解x最佌的难题?br>
         但是BFS和DFS一样不能解决问题c Q因为每个状态都需要扩张,所以广搜很Ҏ使待搜状态的数目膨胀。最l媄响效率?br>

Q6Q该如何减少因广搜所扩张的与目标状态及解无关的状态?
         前面所说的都是从START状态向END状态搜索,那么Q将END状态与START状态倒一下,其实会有另一条搜索\径(Q8{略三讨论)Q但单的交换END与STARTq不能羃状态膨胀的规模。我们可以将正向与反向的搜烦l合hQ这是双向q度搜烦?br>         双向q搜是指同时从START和END两端搜,当某一端所要访问的一个状态是被另一端访问过的时候,x到解Q搜索结束。它的好处是可以避免q搜后期状态的膨胀?br>采用双向q度搜烦可以空间和旉节省一半!


Q7Q决定一个快的检索策略?
         双向q搜能大大减时间和I间Q但在有的情况下我们q不需要空间的节省Q比如在Q4中已l决定了我们需要用的I间?Q,所以不需要节省。这h们可以把重点攑֜旉的羃短上?br>         启发式搜索是在\径搜索问题中很实用的搜烦方式Q通过设计一个好的启发式函数来计状态的优先U,优先考虑优先U高的状态,可以提早搜烦到达目标态的旉。A*是一U启发式搜烦的,他的启发式函数f ' ()=g' () + h' () 能够应用到八数码问题中来?br>         g' () ----- 从v始状态到当前状态的实际代hg*()的估计|g' () >= g*()
         h' () ----- 从当前状态到目标状态的实际代hh*()的估计|h' () <= h*()
注意两个限制条gQ?br>         Q?Qh' () <= h*()   Q?QQ意状态的f '()值必d于其父状态的f '()|即f '()单调递增?br>
         其中Qg' () 是搜索的深度Q?h' () 则是一个估计函敎ͼ用以估计当前态到目标态可能的步数。解八数码问题时一般有两种估计函数。比较简单的是difference ( Status a,   Status b )Q?其返回值是a 和b状态各位置上数字不同的ơ数。另一U比较经典的是曼哈顿距离    manhattan ( Status a, Status b )Q其q回的是各个数字从a的位|到b的位|的距离Q见例子Q?br>
例如状?1 3 7 2 4 6 8 5 2 和状?1 2 3 4 5 6 7 8 9 的difference ?Q不含空|。而他的manhattan 距离是:
1 (7d一? + 1 (2u一? + 2 (4l两次) + 3 (6r两次u一? + 2 (5u一ơl一? = 9
单个数字的manhattan应该于5Q因为对角的距离?Q若大于4则说明计有误?br>
         无论是differenceq是manhattanQ估计ؓ小接qENDQ所以优先高?br>         在计difference和manhattanӞ推荐都将I格忽略Q因为在difference中空格可有可无,Ҏ体搜索媄响不大?br>
         本文后面的实现将使用manhattan 不计I格的方法。其实,每移动一步,不计I格Q相当于Ud一个数字。如果每ơ移动都是完的Q即把一个数字归位,那么START态到END态的距离是manhattan。反q来_manhattan是START到END态的臛_走的步数?br>         回到f '()=g' ()+ h' ()Q其实广度搜索是h' ()=0的一U启发式搜烦的特例,而深度搜索是    f ' ()=0 的一般搜索。h' ()对于优化搜烦速度有很重要的作用?br>
Q8Q能否进一步优化检索策略?
         {案是肯定的?br>         A*搜烦{略的优劣就是看h' ()的决定好坏。前面列举了两个h' ()的函敎ͼ但光有这两个是不够的。经q实验分析,在f '()中,g '()军_的是START态到END态中求得的解距离最优解的距R?而h' () 能媄响搜索的速度?br>         所以优化的W一条策略是Q放大h' ()Q比如,让h '()= 10* manhattan()Q那么f '()= g' ()+10*manhattan()Q可能提高搜索速度。可惜的是所得的解将不再会是最优的了?br>         Z么放大h'()能加快搜索速度Q我们可以想象一下,h'()描述的是本状态到END态的估计距离Q估计距越短自然快一点到达END态。?g' ()描述的是目前的深度,攑֤h' ()的目的是量忽略深度的因素,是一U带{略的深搜,自然速度会比q搜和深搜都快,而因为减考虑了深度因素,所以离最优解p来越q了。关于h' ()攑֤多少Q是很有的问题Q有兴趣可以做些实验试试?br>
         W二条是更新待检查的状态,׃A*搜烦会需要一个待查的序列。首先,在Q4已经提到用哈希避免重复访问同一状态。而在待检查队列中的状态是未完成扩张的状态,如果出现了状态相同但其g '()比原g '()的情况,那么我们更希望的是搜索新状态,而不是原状态。这P在待查队列中出现重复状态时Q只需更新其g'() 可以了?br>
         W三条是注意实现E序的方法,在v初我用sort排序f '()后再扑և权值最大的状态,而后发现用make_heap要更快。想一惻I׃需要访问的接点较多Q待讉K队列一大那么自然反复排序对速度会有影响Q而堆操作则比排序更好。另一ҎQ实现更新待查队列时的搜索也要用比较好的Ҏ实现。我在JAVA的演C程序中用的PriorityQueueQ可是结果不是很令h满意?br>
         W四条优化策略是使用IDA*的算法,q是A*法的一U,ID名ؓIterative deepening是P代加q意思。思想是如下:
Z准备一个记录一ơ@环最值的temp=MAX, h' ?manhattan距离
     先估从START态到END态的h'() 记录为MINQ将START攑օ待访问队?br>     d队列下一个状态,到队列尾则GOTO?br>     若g'() > MIN GOTO ?br>     若g'() + h'()   > MIN 是否为真Q真GOTO ⑥,?GOTO ?br>     扩展该状态,q标记此状态已讉K。找到END态的话就l束该算法。GOTO ?br>     temp = min(manhattan , temp),GOTO ?br>     若无扩展q状态,MIN=temp QID的意思在q里体现Q从头开始@环GOTO ?br>
         W五条优化策略本w与搜烦无关Q在做题时也没能帮上忙,不过从理Z讲是有参考h值的。记得Q6中介l的从END开始搜起吗Q如果我们的d是对多个START 与ENDq行搜烦Q那么我们可以在每搜索完一ơ后记录下\径,q个路径很重要,因ؓ在以后的搜烦中如果存在START和END的\径已l被记录q了Q那么可以直接调出结果?br>         从END搜vQ可以方便判断下一ơ的START是否已经有\径到END了。当前一ơ搜索完Ӟ其已讉K状态是可以直接使用的,若START不在其中Q则从待讉K的状态链表中按搜索策略找下一个状态,{于接着上一ơ的搜烦l果开始找?br>         之所以没能在速度上帮上忙Q是因ؓq个优化{略需要加大g' ()的比重,否则很容易出现深度相当大的情况,׃前一ơ搜索的{略与下一ơ的基本无关Q导致前一ơ的路径无法预料Q所以就会出现深度过大的情况。解x法是加大g' ()?br>{略五类似给E序加一个缓冲区Q避免重复计。如果要做八数码的应用,~冲Z帮上忙的?br>
Q10Q怎样记录扑ֈ的\径?
         当找到解的时候我们就需要有cM回退的工作来整理一条解路径Q由于用了不是单的DFSQ所以不能借助通过函数调用所是用的E序栈?br>         我们可以手工加一个模拟栈。在Q4中解决了哈希的问题,利用哈希表就能快速访问状态对应的|在这里,我们把原来的bool值改为char或其他能表示一ơ操作(臛_需?U状态,除了u r l d 外还要能表示状态已讉KQ的值就行了?br>         在搜索到解时Q记录下最后一个访问的状态|然后从读取该状态对应的操作开始,像栈操作的退栈一P不停往回搜Q直到找到搜索v点ؓ止。记录好栈退出来的操作|是一条\径?
 
 
 
 以前看了刘汝佳的书上的A*法Q一直不知道怎么写,可以参考下面这个模型?/font>
关于八数码问题可以有很多U实玎ͼq只是其中一U?/font>
A*法求八数码问题{转}
2008-05-08 09:18

Q?Q?用A*法Q?Ch函数?#8220;Hamilton距离”比选用“在错误方各内的数字数?#8221;强很多?
Q?QA*法的关键是要能够“扑ֈ耗费最的节点”?#8220;判断是否子节点已l存?#8221;的算法的效率可能高Qؓ此,|上不少资料采用Binary Heap或Hash table{数据结构,然而单独用Q何一U数据结构,都有一定的~陷Q将Heap和Hash联合h使用Q应该可以提高不效率。具体如下:
1。徏一张Hash表,存放没一个节点的具体信息Q如当前状态、父节点在表中的位置{?
2。open表中存放着一些节点,q些节点不同于Hash表中的节点,可以它理解为是Hash表中节点的烦引,它只包含节点的估价和节点在Hash表中的位|,q些节点按估h列成堆(HeapQ?
3。没必要再徏一个closed表?
q样Q程序的程可以写成:
初始化Hash表和open表;
根节点攑օHash表和open表;
found = false;
while(open表不I? {
      从open表中得到耗费最低的节点的烦引cur; // O(1)
      从open表中删除耗费最低的节点; // O(logN)
      for 每个cur的子节点now do {
           if ( now 是目标节?) {
                   打印输出;
                   found = true;
                   l束搜烦;
           }
           if( now 不在Hashtable?) { // O(1)
                now插入Hashtable? // O(1)
                now的烦引放入open表中; // O(logN)
           } else if( now比Hashtable中的节点?) {
                if( 原节点在open表中 ) { // O(N)
                     用now替代原来节点; // O(1)
                     在open中push_heap来更新open表的序; // O(logN)
                 }
           }
       }
   }
   if( !found ) 输出 "无解";
可以看到Q虽然查找open表中已存在的节点仍然为O(N)Q?但是当now已存在时Qnow比open表中节点优的概率是很的Q事先用O(1)的时间判断,可以避免用O(N)的时间来查找Q从而提高了效率Q这很像是CPU的Cache的工作原理)?
具体E序如下Q?/p>

#include "stdafx.h"
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <cassert>
using namespace std;
struct OpenNode{
int cost, point;
OpenNode(){}
OpenNode( int cost, int point ){
   this->cost = cost;
   this->point = point;
}
};
bool operator<( const OpenNode& a, const OpenNode& b ){
return a.cost > b.cost;
}
bool operator==(const OpenNode& a, int c){
return a.point == c;
}

struct HashNode{
char state[3][3];
int g, h, par, dir,x,y;
bool used;
HashNode(){
   used=false;
}
};

int dx[] = { -1,0,1,0 },
dy[] = { 0,1,0,-1 }; // u r d l

const int HASH_SIZE = 39999;
HashNode List[HASH_SIZE];
int factorial[] = {1,1,2,6,24,120,720,5040,40320};
/*int hash( char state[3][3] ){
int ret = 0;
char *p, *q;
for(p=&state[0][0];p<=&state[2][2];p++){
int cnt = 0;
for(q=&state[0][0]; q<p; q++) if( *q<*p ) cnt++;
ret += factorial[&state[2][2]-p]*(*p-cnt);
}
return ret;
}*/
bool eq( char a[3][3], char b[3][3] ){
for(int i=0;i<3;i++)
   for(int j=0;j<3;j++) if( a[i][j]!=b[i][j] ) return false;
   return true;
}
int hash( char state[3][3] ){
char* p = &state[0][0];
int ret = p[0] * 7 + p[1] * 17 + p[2] * 47 + p[3] * 117 + p[4] * 217 + p[5]
   * 977 + p[6] * 1299 + p[7] * 5971 + p[8] * 7779;
ret %= HASH_SIZE;
while( List[ret].used && !eq(List[ret].state , state) )
   ret= (ret+1) % HASH_SIZE;
return ret;
}
int h( char state[3][3] ){
int ret = 0;
for(int x=0;x<3;x++)
   for(int y=0;y<3;y++) if( state[x][y]!=0 )
    ret += abs((state[x][y]-1)/3-x) +abs((state[x][y]-1)%3-y);
   return ret;
}
void output( int i ){
string res;
while(List[i].dir>=0){
   res+= List[i].dir==0?'u':List[i].dir==1?'r':List[i].dir==2?'d':'l';
   i = List[i].par;
}
reverse( res.begin(), res.end() );
cout<<res<<endl;
}
bool solvable( char state[3][3] ){
int t = 0;
for(char* i=&state[0][0];i<&state[2][2];i++)
    for(char* j=i+1;j<=&state[2][2];j++)
      if(*i!=0 && *j!=0 && *i>*j) t++;
if(t%2) return false;
return true;
}
vector<OpenNode> open;
int main(){
string init;
getline( cin, init );
HashNode now;
int cnt=0;
for(int i=0;i<init.size();i++) {
   if(init[i] == 'x') {
    now.state[cnt/3][cnt%3] = 0;
    now.x = cnt /3;
    now.y = cnt %3;
    cnt++;
   } else if(init[i]!=' ') {
    now.state[cnt/3][cnt%3] = init[i] - '0';
    cnt++;
   }
}
if( !solvable(now.state) ) {
    cout<<"unsolvable"<<endl;
    return 0;
}
now.g = 0;
now.h = h(now.state);
now.par = -1;
now.dir = -1;
now.used = true;
int i = hash(now.state);
List[i] = now;
open.push_back(OpenNode(now.g+now.h,i));
while(!open.empty()){
   int cur = open.front().point;
   pop_heap( open.begin(), open.end() );
   open.erase( open.end()-1 );
   for(int i=0;i<4;i++){
    now = List[cur];
    int x = now.x+dx[i];
    int y = now.y+dy[i];
    if(x<0||x>2||y<0||y>2) continue;
    swap( now.state[x][y], now.state[now.x][now.y] );
    now.x = x;
    now.y = y;
    now.h = h( now.state );
    now.g++;
    now.par = cur;
    now.dir = i;
   
    int hashcode = hash( now.state );
    if( now.h == 0 ){
     List[hashcode] = now;
     output( hashcode );
     return 0;
    } else if( !List[hashcode].used ){
     List[hashcode] = now;
     open.push_back( OpenNode(now.h+now.g,hashcode) );
     push_heap( open.begin(), open.end() );
    } else if( List[hashcode].g+List[hashcode].h > now.g+now.h ){
     List[hashcode] = now;
     vector<OpenNode>::iterator it = find( open.begin(), open.end(), hashcode );
     if(it==open.end()) continue;
     push_heap( open.begin(), it+1 );
    }
   }
}
cout<<"unsolvable"<<endl;
return 0;
}



Kevin_Zhang 2010-08-21 19:57 发表评论
]]>
99þۺϺݺۺϾþ| Ʒ׾þAAAƬ69| Ʒþþþþø| þAV뾫Ʒɫҹ| þþƷϵ| Ʒݾþþþ| þþþþ޾Ʒ| Ʒþþþþ벻| þþþþþþþþþĻ| þ¾Ʒ| ĻþþƷˮ| ѾƷ99þùۺϾƷ| ˾þô߽AVһ| 99þþƷ| þþþ߽ۺϳ| ˾þۺߴý| Ʒþþһ| Ʒþþþþ| 69Ʒþþþվ| ŷ龫Ʒþþþþþþžž| þùۺϾƷ | þþþþþþþþ| AVþüįٸ| þþƷ| aëƬþ| ˳ŷþ| þseƷһƷ| 99þó˹Ʒ| ɫ8þ97㽶987| ھƷþۺ88| þëƬѿһ| 91ɫۺϾþ| þ޴ɫĻþþ| ޾Ʒھþ| þݺҹҹav˳| ˾þô߽Ʒ| ˾þþþƷ| ݺɫۺþö| þþƷһ| ձŷþþþѲ| ҹҹݺݾþö|