程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 馬走日棋盤算法

馬走日棋盤算法

編輯:關於VC++

問題描述

在給定大小的方格狀棋盤上, 將棋子”馬”放在指定的起始位置 , 棋子”馬” 的走子的規則為必須在棋盤上走”日”字; 從棋子”馬”的起始位置開始, 搜索出一條可行的路徑, 使得棋子”馬”能走遍棋盤上的所有落子點, 而且每個落子點只能走一次;

例如: 棋盤大小為5*5 , 棋子馬放的起始落子點為 ( 3 , 3 ) ; 算法需要搜索一條從位置( 3 , 3 ) 開始的一條包括從( 1 , 1 ) , ( 1 , 2 ) , ( 1 , 3 ) … ( 5 , 1 ) , ( 5 , 2 ) , ( 5 , 3 ) , ( 5 , 4 ) , ( 5 , 5 ) 總共25個可以落子的全部位置;

問題分析

通過上面的問題描述,我們對問題的內容有了正確的理解,接下來我們開始對問題進行具體細致的分析,以求找到解決問題的正確的可行的合理的方法;

首先我們需要在程序中用合適的數據結構表示在問題中出現的棋盤 , 棋子 , 棋子的走子過程 ; 接下來我們需要對核心問題進行分析, 即如何搜索一條可行的路徑 , 搜索采取何種策略 , 搜索的過程如何表示 ;

對於一個大小為n*m大小的棋盤 , 棋子從當前位置( x , y ) 出發,可以到達的下一個位置( x’ , y’ ):

(1) ( x +1 , y +2 )

(2) ( x +1 , y –2 )

(3) ( x – 1, y +2 )

(4) ( x – 1, y – 2 )

(5) ( x +2, y +1)

(6) ( x +2, y – 1)

(7) ( x -2, y + 1)

(8) ( x -2, y – 1 )

限制條件:

1. 1 <= x’ <= n , 1 <= y’ <= m; ( n : 棋盤的高度 , m: 棋盤的寬度 );

2. ( x’ , y’ ) 必須是棋子記錄表中沒有包括的新位置;

3. 棋子走子過程記錄表中沒有包括棋盤上的所有可以落子的位置;

對這個過程不停迭代的過程也就是對解空間搜索的過程, 搜索直到棋子走子記錄表中包括棋盤上的所有可以落子的位置 , 就搜索到了一條可行的路徑,路徑包括棋盤上的所有落子點;或者搜索完整個解空間,仍然找不到一條可行的解,則搜索失敗;

下面我們舉例來說明搜索的過程;

棋盤大小 : 5 * 5

棋子起始位置 : ( 3 , 3 )

搜索過程 :

(1) 從當前位置( 3 , 3 )出發可以有8個新的位置選擇; 首先選擇新位置1 , 將新位置1

作為當前棋子位置 , 開始新的搜索;

如果搜索不成功, 則搜索回退, 選擇新位置2 ,以此類推,就可以搜索完整個解空間,只要從該問題有解 , 則可以保證一定可以搜索到;

2) 從新位置1 開始新的搜索,可以選擇的新位置有兩個,先選擇位置1 , 從位置1開始新的搜索;

(3) 下圖是經過18步搜索之後的狀態, 從位置18出發, 已經沒有沒走過的新位置可以選擇, 則搜索失敗;

搜索回退到17步, 從位置17開始搜索除了18之外的新位置, 從圖上可以看出已經沒有新位置可以選擇,繼續回退到16步, 搜索除了17的新位置; 以此類推.知道搜索完整個解空間 , 或者搜索到一個可行解;

(4) 下圖展示了搜索成功的整個搜索過程;

系統設計

一. 用例圖

二. 類設計

三. 順序圖

四. 核心算法設計

通過上面的分析, 我們現在可以將算法的大概框架寫出來了 , 具體的代碼請參考本文章後面的源程序;

下面我們先列出了經典回溯算法的框架; 由於考慮到程序實現的方便性 , 所以本文中采用的回溯算法對經典算法進行了適當的修改;

經典算法:

void BackTrack( int t )
{
  if ( t > n ) OutPut( x );
  else
    for( int I = f( n , k ) ; i <= g( n , k ) ; i ++ )
    {
      x[ t ] = h ( i );
      if ( ConsTraint( t ) && Bound( k ) )
        BackTrack( k + 1 );
    }
}
本文采用的算法:bool Search( Location curLoc )//開始計算;
{
    m_complex++;
    //修改棋盤標志;
    m_chessTable[ curLoc.x-1 ][ curLoc.y-1 ] = 1;
    //是否搜索成功結束標志;
    if( isSuccess() )
       return true;
    //還有未走到的棋盤點,從當前位置開始搜索;
    else
    {
       //遞歸搜索未走過的棋盤點;
       for( int i = 0 ; i < 8 ; i++ )
       {
           Location newLocation = GetSubTreeNode( curLoc , i ) ;
           if( isValide( newLocation ) &&
      m_chessTable[newLocation.x-1][newLocation.y-1] == 0 )
           {
              if( Search( newLocation ) == true )
              {
                  //填寫記錄表;
                  MarkInTable( newLocation, curLoc );
                  return true;
              }
           }
       }
    }
    //搜索失敗,恢復棋盤標志;
    m_chessTable[curLoc.x-1][curLoc.y-1] = 0;
    return false;
}

測試數據和測試結果

(1). 測試數據1 :

棋盤大小         棋子起始位置 ( 1 , 1) ( 4 , 4 ) ( 2 , 3 ) 略… 搜索到的可行解 無 無 無 無 搜索解空間大小 2223 2223 501 略…

結論: 對於4 * 4 和小於 4*4的棋盤,此問題無可行解;

(2). 測試數據2:

棋盤大小 : 5 * 5

棋子起始位置 : ( 1 , 1 )

搜索解空間大小 : 76497

搜索結果圖示 :

棋子起始位置 : ( 3 , 3 )

搜索解空間大小 : 11077

搜索結果圖示 :

結論: 對於5*5 的棋盤 ,此問題有可行解,搜索解空間大小隨棋子的起始位置不同而不同,從某些位置起始搜索 , 此問題可能沒有可行解;

(3). 測試數據3 :

棋盤大小 : 6 * 6

棋子起始位置 : ( 4 , 2 )

搜索到的可行解 : 2029720

結果圖示 :

(4). 測試數據4:

棋盤大小 : 7 * 7

棋子起始位置 : ( 3 , 3 )

搜索解空間大小 : 12799463

結果圖示 :

結論

通過多組數據的測試,我們發現當棋盤的高度 height <= 5 , 寬度 width <= 5 的時候, 該棋盤問題的解空間比較小,本文采用的算法可以在很短的時間內搜索完整個解空間 ;

當棋盤為5*5 大小 , 整個解空間大小為1829421 = 2 (21) ; 由於棋盤和棋子的一些特點 ( 如: 棋子從當前位置出發只能到達棋盤上的某些特殊點, 而且這些點必須不包含在走子記錄表中), 這就給分析棋盤算法的時間復雜度帶來了一些困難, 我們只能通過不同大小棋盤的特點來大概分析算法的時間復雜度, 通過實際的測試(在棋盤大小為5*5 ), 估算的時間復雜度與實際的復雜度基本在一個數量級;

上圖是一個5*5大小的棋盤 , 方框所在的位置 ( 3 , 3 )出發可以到達的點有8個, 而下次從8個新的搜索點出發平均能到達的有2個點 , 還有25 – 1 – 8 = 16 個點 , 16個點中除去4個點就剩一般的點沒有走過, 從這4個點出發, 可以到達的新的搜索點平均有2個, 當棋盤上的一半以上的點全都走過,則從剩余的12個點出發可以到達的新的搜索點平均只有1;

通過上面的分析 , 我們可以得出 Space( 5*5 ) = 8 * pow( 2, 8 ) * pow( 2 , 4 ) * 12 = pow( 2 , 20 );

同理,我們可以對棋盤大小為8*8 的解空間大小進行估算; 當然估算當中的一些特殊點的選擇是需要一些技巧和實際經驗的, 雖然最終結果可能不准確 , 但是能夠保證基本在一個數量級上;

Space( 8 * 8 ) = pow( 4 , 8 ) * pow( 4 , 4 ) * pow( 2 , 20 ) * pow( 2 , 32 );

可以看出,解空間是相當大的, 我們假設計算機每分種搜索300萬步 , 對於棋子”馬”給定一個起始位置, 要想證明此問題無解 , 則需要搜索的時間為 ( 下面數字均為估計值 ) :

Time( 8 * 8 ) = Space ( 8 * 8 ) / 300萬 = pow( 2 , 76 ) / 300萬 = pow( 2 , 62 )分鐘 =

pow( 2 , 56 )天 = pow ( 2 , 47 )年 = 128億年

注: pow( x , y ) 代表x的y次方;

可見要搜索完一個大小為8*8 棋盤問題的全部解空間是根本不可能的;

算法的時間復雜度為pow( 2 , n ) , 是個NP難解問題;

具體算法實現請參考本文配套源代碼。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved