八数码的几种解决.docx

上传人:b****8 文档编号:9869627 上传时间:2023-02-07 格式:DOCX 页数:54 大小:29.10KB
下载 相关 举报
八数码的几种解决.docx_第1页
第1页 / 共54页
八数码的几种解决.docx_第2页
第2页 / 共54页
八数码的几种解决.docx_第3页
第3页 / 共54页
八数码的几种解决.docx_第4页
第4页 / 共54页
八数码的几种解决.docx_第5页
第5页 / 共54页
点击查看更多>>
下载资源
资源描述

八数码的几种解决.docx

《八数码的几种解决.docx》由会员分享,可在线阅读,更多相关《八数码的几种解决.docx(54页珍藏版)》请在冰豆网上搜索。

八数码的几种解决.docx

八数码的几种解决

八数码的几种解决

帖子关于是八数码的,是前段时间写的,为了给别人一些搜索的样例。

 现在,事差不多完了,代码还留着,觉着放着浪费,又想自己水平过于欠缺,再加上看在C吧的初学者还是占主要,就觉得发在此处共他们学习还是不错的,仅此而已。

如果有不知道八数码的问题的, 可以上网一搜,就什么都明白了。

 在这里我只用了几种算法实现,其实还有其他方法,我并没有写(如果有人需要,可能我会重新完成)。

首先分析下八数码的一些算法吧。

1:

 深度优先(DFS):

      这个方法起始对八数码非常不好,问题在于搜索过于盲目,搜索深度如果没有限制,有些是根本没必要的(因为八数码的解,大部分集中在20~30步之间)。

而且即使搜索到一个解,很可能不是最优解。

  所以用DFS解决这个问题并不明智,不过为了显示其不好,我仍然实现,不过在程序中设置最大深度限制为100。

2:

 宽度优先搜索(BFS)

     宽度优先搜索,解决的还不错,经过对hash函数的优化,能使一个20~30步的解在200~300MS内解决(不过其还可以做进一步的优化)。

3:

 迭代加深搜索

     迭代加深搜索,可以说是吸取了宽搜和深搜的优点,同时避免了深搜搜到第一个解的不最优性,和宽搜的使用较大的内存空间。

 

     在这里我也用迭代加深搜索, 仔细一看的人就发现,起使他只是在深搜上稍微做点“手脚”就完毕,但是其思想,虽然显而易见,可我觉得并不容易想到(指当初提出这个算法的时候)。

4:

 爬山法

    爬山法,经常在人工只能里面见得到,由于其高效,实现简单,所以其有时也是很有用的。

但是爬山法,也有缺点,就是搜到的解只是局部最优解,而且有陷入“无解”的风险。

 爬山法,顾名思意就是一直沿着“最好的方向走”直到“山顶”,就找到解。

 爬山法,不像回溯, 在遍历节点时不保存其他非当前最优节点,也就是说,一旦走错了,就不能再后头搜索其他方向。

    所以在我写的爬山法中,对于一个解,虽然很多100多步,但是所需的时间确实0MS左右。

5:

 双向搜索

   双向搜索,前后来搜,碰到头,然后一拼接就出了路径。

其搜索的节点数要明显必宽搜少许多,所以在程序中,我并未对其做任何优化,他搜索一个解的时间基本上是0MS。

6:

A*算法

   A*算法,不用说,解决这个问题很简单。

速度也快,都是0MS。

具体的可以参考网上的解释。

7:

DIA算法

   DIA算法可以说是 A* 与 迭代搜索的结合,思想来源于迭代搜索,其实简单的说就是,迭代搜索添加了估价函数。

由于时间,当初我并未用该方法实现。

 

第一个贴的代码是宽搜,由于在hash上做了优化,所以对hash部分做下解释:

******************************************

对于一个状态,转换为 1 2 3 4 0 5 6 7 8 这样的序列,然后分别对应权值:

0 :

 8!

  , 1 :

7!

  2 :

 6!

 , .... ,  8:

0 !

然后用每个数的逆序数乘上权值的和即为改状态的hash表对应的下标。

对于上序列对应的hash下标为:

 4 * 8!

 + 0 * 7!

 + 0 * 6!

 + 0 * 5!

 + 0 * 4!

 + 0 * 3!

 + 0 * 2!

 + 0 * 1!

 + 0 * 0!

 = 161280

----------------------------------------------------------------------

另外对于这种hash值计算外,还有一个由上一个状态来推到下一个状态的hash值的规律:

假设,移动是有从空格的移动方向来看,并且设空格在序列中的位置为 i,并且是index为上一个状态的hash值,则下一个状态的hash值为:

               上移:

index - 3*8!

 + a[i-3]!

 * ( a[i-1] > a[i-3] + a[i-2] > a[i-3] ) - a[i-2]!

 * ( a[i-2] < a[i-3] ) - a[i-1]!

 * ( a[i-1] < a[i-3] );

           

           下移:

index + 3*8!

 - a[i+3]!

 * ( a[i+1] > a[i+3] + a[i+2] > a[i+3] ) + a[i+2]!

 * ( a[i+2] < a[i+3] ) + a[i+1]!

 * ( a[i+1] < a[i+3] );

newindex = 

            左移:

index - 8!

           右移:

index + 8!

上移newindex值的计算程序如下计算(假设,Factorial[9],分别记录了(8-i)!

的值):

newindex -= 3*40320 ; 

newindex += ( p[ i - 2 ] > p[ i - 3 ]) ?

 ( Factorial[ p[ i - 3 ] ] ) :

 ( - Factorial[ p[ i - 2 ] ] );

newindex += ( p[ i - 1 ] > p[ i - 3 ]) ?

 ( Factorial[ p[ i - 3 ] ] ) :

 ( - Factorial[ p[ i - 1 ] ] );

下移:

newindex += 3*40320 ; 

newindex -= ( p[ i + 2 ] > p[ i + 3 ]) ?

 ( Factorial[ p[ i + 3 ] ] ) :

 ( - Factorial[ p[ i + 2 ] ] );

newindex -= ( p[ i + 1 ] > p[ i + 3 ]) ?

 ( Factorial[ p[ i + 3 ] ] ) :

 ( - Factorial[ p[ i + 1 ] ] );

因此,对于此种hash值的计算,最多只需要3次加减法,2次比较(平均约2次加减法,3.5次比较运算)运算即可完成hash值的计算,而且这种计算出来的hash值不存在冲突。

********************************************

宽搜代码如下:

#include 

#include 

#include 

#include 

#include 

using namespace std;

#define HashTableSize 362880

#define NOT !

typedef struct maps

{

    int detail[3][3];

    int parent;            // 记录父节点在hash表中的位置

    int myindex;        // 记录自己节点在hash表中的位置

    int x, y;            // 记录 空格(0)的坐标

}Map,*PMap;

Map   org;                //  初始状态

Map   end;                //    目标状态

PMap  HashTable[HashTableSize]={NULL};        //hash表

char  Rpath[150];                    //最终的路径

int const derection[4][2] ={ { -1 , 0 }  , {1, 0 }, { 0 , -1 } , { 0, 1 } } ;  // 可移动的四个方向

bool  FlageNew;

/**

 *

 *    八数码的输入(在这里不做任何输入检查,均认为输入数据是正确的)

 *

 **/

void input()

{

    int i,j;

    int sum;

    for(i = 0 ; i < 9 ; 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 ; i < 9 ; i ++ )                //计算逆序

    {

        if( 0 == *(*org.detail + i) ) 

            continue;

        for(j = 0 ; j < i;  j ++ )

 if( 0 !

= *(*org.detail+j) && *(*org.detail + j) < *(*org.detail + i) ) 

            {

                sum ++; 

            }

    }

    if( sum%2 == 0 )        // 目标状态各个数字对应的坐标位置

    {

        end.detail[0][0] = 1 , end.detail[0][1] = 2 , end.detail[0][2] = 3 ;

        end.detail[1][0] = 4 , end.detail[1][1] = 5 , end.detail[1][2] = 6 ;

        end.detail[2][0] = 7 , end.detail[2][1] = 8 , end.detail[2][2] = 0 ;

    }

    else

    {

        end.detail[0][0] = 1 , end.detail[0][1] = 2 , end.detail[0][2] = 3 ;

        end.detail[1][0] = 8 , end.detail[1][1] = 0 , end.detail[1][2] = 4 ;

        end.detail[2][0] = 7 , end.detail[2][1] = 6 , end.detail[2][2] = 5 ;

    }

    return;

}

/**

 *

 *检测两个状态是否一样

 *

 **/

inline bool IsEqual(Map a , Map b)

{

    return 0 == memcmp((const void *)(*a.detail),(const void *)(*b.detail),36);

}

/**

 *

 * hash值的计算

 *

**/

int HashValue(Map a)   

{

   int count  ; 

   int i , j ;

   int value =0 ;

   static int pv[9]={1,1,2,6,24,120,720,5040,40320};

   for(i = 0 ; i < 9 ; i ++ )

   {

       for(j = 0, count =0 ; j < i ; j ++ )

       {

            if( *(*a.detail+i) < *(*a.detail+j) ) 

            {

                count ++;

            }

       }

       value += pv[i]*count;

   }

    return value;

}

/**

 *

 *状态插入到hash表中。

返回的是插入到的hash表中对应的下标值

 *

 **/

int InsertHashTable(Map a , int parent)

{

    int index = HashValue( a );

    if(NULL == HashTable[index])  //如果为TURE,那么说明hash表中不存在该状态,应该插入到hash表中

    {

        a.parent = parent;

         HashTable[index] = new Map;

        *HashTable[index] = a;

        FlageNew = true;

    }

    else

    {

        FlageNew = false;

    }

    return index;

}

/**

 *

 *    宽度优先搜索

 *

 **/

void Bfs()

{

    queue Queue;

    bool Finish = false;

    org.myindex = InsertHashTable( org , -1 );

    Queue.push(org);

    while(    false == Queue.empty() && NOT Finish )

    {

        Map node  = Queue.front();

        Queue.pop();

    //    printf("%d\n",Queue.size());

        if( true == IsEqual(node , end ) )

        {

            Finish = true;

            end.parent = node.parent;

            end.myindex = node.myindex;

            continue;

        }

        for(int k =0 ; k < 4; k ++ )

        {

            Map tmp = node;

            tmp.x = node.x + derection[k][0],

            tmp.y = node.y + derection[k][1];

            if(tmp.x < 0 || tmp.x > 2 || tmp.y <0 || tmp.y >2 ) 

            {

  continue;

            }

            tmp.detail[ node.x ][ node.y ] = tmp.detail[ tmp.x ][ tmp.y ];        //移动空格

            tmp.detail[ tmp.x ][ tmp.y ] = 0 ;

            int tmpindex = InsertHashTable( tmp , node.myindex );

            if( false == FlageNew )        //如果不是新状态的,即以前访问过改节点,不再加入队列中

            {

                continue;

            }

            tmp.parent = node.myindex;

            tmp.myindex = tmpindex;

            Queue.push(tmp);

        }

    }

    return ;

}

/**

 *

 * 通过hash表中记录的进行查找路径

 *

 **/

void FindPath()

{

    Map now;

    stack Stack;

    Stack.push(end);

    now = end;

    int count=0;

    while(now.parent !

= -1 )

    {

        Stack.push(*HashTable[now.parent]);

        now = Stack.top();

    }

    printf("共需:

 %d 步\n",Stack.size()-1);

    getchar();

    while( NOT Stack.empty())

    {

        now = Stack.top();

        Stack.pop();

        for(int i =0 ; i < 3; i ++ )

        {

            for(int j =0 ; j < 3; j  ++)

            {

                printf("%3d",now.detail[i][j]);

            }

            printf("\n");

        }

        if(0 !

= Stack.size() )

        {

            printf("\n    ↓ 第%d步\n",++count);

            getchar();

        }

    }

    printf("\nThe End!

\n");

    return ;

}

int main()

{

    input();

    long time =GetTickCount();

    Bfs();

    printf("计算用时:

%dMS\n",GetTickCount()-time);

    FindPath();

    return 0;

}

接下来的第二个代码是:

 

 

深度优先搜索(限制了他的深度为50)

#include 

#include 

#include 

#include 

#include 

using namespace std;

#define HashTableSize 362880

#define NOT        !

#define MaxDeep 50

typedef struct maps

{

    int detail[3][3];

    int x, y;            // 记录 空格(0)的坐标

}Map,*PMap;

Map   org;                //  初始状态

Map   end;                //    目标状态

bool  HashTable[HashTableSize]={false};        //hash表

int const derection[4][2] ={ { -1 , 0 }  , {1, 0 }, { 0 , -1 } , { 0, 1 } } ;  // 可移动的四个方向

int   Path[ MaxDeep + 1 ];

int   Step;

bool  Finish;

/**

 *

 *    八数码的输入(在这里不做任何输入检查,均认为输入数据是正确的)

 *

 **/

void input()

{

    int i,j;

    int sum;

    for(i = 0 ; i < 9 ; i ++ )

    {

        scanf("%1d", *org.detail + i );

        if(0 == *(*org.detail + i) )

        {

            org.x = i / 3;

            org.y = i % 3;

        }

    }

    for(i = 0 ; i < 9 ; i ++ )                //计算逆序

    {

        if( 0 == *(*org.detail + i) ) 

            continue;

        for(j = 0 ; j < i;  j ++ )

            if( 0 !

= *(*org.detail+j) && *(*org.detail + j) < *(*org.detail + i) ) 

            {

 sum ++; 

            }

    }

    if( sum%2 == 0 )        // 目标状态各个数字对应的坐标位置

    {

        end.detail[0][0] = 1 , end.detail[0][1] = 2 , end.detail[0][2] = 3 ;

        end.detail[1][0] = 4 , end.detail[1][1] = 5 , end.detail[1][2] = 6 ;

        end.detail[2][0] = 7 , end.detail[2][1] = 8 , end.detail[2][2] = 0 ;

    }

    else

    {

        end.detail[0][0] = 1 , end.detail[0][1] = 2 , end.detail[0][2] = 3 ;

        end.detail[1][0] = 8 , end.detail[1][1] = 0 , end.detail[1][2] = 4 ;

        end.detail[2][0] = 7 , end.detail[2][1] = 6 , end.detail[2][2] = 5 ;

    }

    return;

}

/**

 *

 *检测两个状态是否一样

 *

 **/

inline bool IsEqual(Map a , Map b)

{

    return 0 == memcmp((const void *)(*a.detail),(const void *)(*b.detail),36);

}

/**

 *

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 文学研究

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1