A 搜尋 八數碼問題

2021-07-23 09:01:53 字數 4185 閱讀 9135

八數碼問題,簡單地來描述是這樣的:在乙個九宮格內,填有1、2、3、4、5、6、7、8,八個阿拉伯數字,有乙個格仔為空白。就下面這樣,這是乙個沒有歸位的九宮格。 12

3784

65(未歸位的九宮格)

對於上面這個九宮格,我們要通過移動數字來使之歸位,每次移動都只能是與空白格相鄰的數字移到空白格裡面。最終得到歸位的九宮格應該是像下面這樣的。 12

3847

65(已歸位的九宮格)

我們的目標是,給出一種初始狀態,如果有解的話,就把移動的步驟記錄(或者輸出)下來。值得注意的是,有的初始狀態,是無論如何都沒有辦法使九宮格歸位的。

問題抽象

對於這個問題,我們不妨這樣來看:每一種狀態,最多有四種移動方式,也就是說,一種狀態最多可以衍生出四種子狀態。我們把問題的整體看成乙個圖,乙個狀態是圖中的乙個節點,每個節點生成的自節點由一條有向邊來指向。圖中總有乙個節點的狀態是我們要的歸位狀態,我們要做的就是找到初始節點(即初狀態)到這個歸位節點的路徑。

圖的寬度優先搜尋

為了找到我們要的路徑,我們可以使用寬度優先搜尋。我們需要三樣東西:集合close,用於儲存訪問過的節點。集合open,用於儲存待訪問的節點。每次訪問乙個節點,就會產生子節點,那麼有如下三種情況:

如果子節點存在於open中,就把該子節點的深度與open中的那個進行對比,若該子節點的深度較小,就替換。

如果子節點存在於close中,就把該子節點的深度與close中的那個進行對比,若該子節點的深度較小,就替換,並且該子節點的所有後代節點深度都要進行修改。(但是在該問題的a*搜尋演算法中,我們可以不用考慮這件事。把弱遇到子節點存在於close中的情況,直接將其扔掉就可以了)

如果子節點不在open中,也不在close,那麼將該節點加入到open中等待訪問。

如此進行下去,一直到找到我們想要的節點(說明有解),或者open變為空為止(說明無解)。

a*搜尋

如果問題有解的話,有沒有什麼辦法可以讓我們的搜尋變得更快呢?

有,我們可以設定乙個評估函式f(n) = g(n) + h(n)g(n)給出的是節點的深度。h(n)它給出的是當前狀態下將所有數字歸位所需的最小步數。每次要從open中取出乙個節點的時候,按照f(n)給節點拍乙個序,然後選取函式值最小的節點。評估函式h(n)所給出的步數,比實際讓九宮格歸位所需的步數要少,所以這種啟發式搜尋叫做a*搜尋。

我們使用乙個整數表示一種狀態,整數中的每一位對應了九宮格中的一位,這樣可以縮減比較九宮格是否一樣所使用的時間。

比如九宮格 :12

3786

54被表示為整數123780654。

資料結構

每乙個節點裡面的資訊包含:

state:當前的狀態、

prev:父節點的指標、

next:乙個存放子節點指標的向量、

cost:h(n)的函式值、

zeropos:空白格仔的位置、

depth:節點深度。

#include 

#include

#include

using

namespace

std;

#ifndef node_h

#define node_h

struct node;

#endif

change函式

輸入乙個狀態,以及空白格仔的位置,以及空白格仔的移動方向,如果移動方向合法,就可以產生子狀態,否則返回-1.

int change(int state,int zeropos,int direction);

int mo = 100000000;

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

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

switch (direction)

case

1: case

2: case

3: default:

break;

}int mul = 100000000;

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

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

return ansstate;

}

h(n)函式

輸入乙個狀態,返回h(n)函式值。

int hn(int t);

for(int i=8;i>=0;i--)

intcount=0;

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

}}

return

count;

}

findzeropos函式

輸入乙個狀態,返回空白格仔的位置。

int findzeropos(int state);

int mo = 100000000;

for(int i = 0;i

<3;i++)

for(int j = 0;j

<3;j++)

for(int i = 0;i

<3;i++)

for(int j = 0;j

<3;j++)

if(matrix[i]

[j]==0)

return

ans;

}

mysearch函式

輸入乙個狀態,用廣度優先搜尋找到使九宮格歸位的路徑,並返回結果節點的指標。路徑用節點組成的鍊錶來儲存。

node* mysearch(int inistate)

//如果存在於open中

bool inopen = false;

for(int j = 0;jif((open[j]->state == nextstate)&&(open[j]->dept>temp->dept+1))

}if(inopen||inclose) continue; //如果不在open且不在close中,則建立新的節點

node *son = new node;

son->state = nextstate;

son->dept = temp->dept+1;

son->cost = hn(nextstate);

son->prev = temp;

son->next.push_back(son);

son->zeropos = findzeropos(nextstate);

open.push_back(son);

}close.push_back(temp);

}return null;

}

主函式(用於測試)
int main();

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

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

cin>>testmatrix[i][j];

//轉換成整數

int test = 0;

int mul = 100000000;

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

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

//搜尋

node* ans = mysearch(test);

//按照九宮格的形式輸出生成的鍊錶

while(ans)

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

cout}

return

0;}

對於有解的情況,該演算法的確可以很快找到使九宮格歸位的路徑。

對於無解的情況,往往要跑很久,遍歷很多節點。程式的執行時間長達十多分鐘。

(完)

搜尋 八數碼問題

搜尋 高階列舉 有順序有策略地列舉狀態空間中的節點,尋找問題 的解 狀態空間 由所有狀態構成的狀態樹 經典八數碼問題 有乙個3 3的棋盤,其中有0 8共9個數字,0表示空 格,其他的數字可以和0交換位置。求由初始狀態 到達目標狀態的步數最少的解?8 2 3 1 4 6 5 7 0 1 2 3 4 5...

廣度優先搜尋 八數碼問題

給定乙個一幅圖和乙個起點s,回答 從s到給定的頂點v是否存在一條路徑?如果有,找出其中最短的那條 所含邊數最少 邊數最少,很自然想到從從經過1條邊能到達的節點有哪些?然後經過這些邊再到達的節點有哪些?這樣我不就能夠想出來最短的路徑了嗎?沒錯,這是非常簡單的想法,然而真正的廣度優先演算法也是這樣,簡單...

八數碼問題 啟發式搜尋

一 問題描述 在乙個3 3 的方棋盤上放置著 1,2,3,4,5,6,7,8 八個數碼 每個數碼佔一格 且有乙個空格。這些數碼可以在棋盤上移動,其移動規則是 與空格相鄰的數碼方格可以移入空格。現在的問題是 對於指定的初始棋局和目標棋局,給出數碼的移動序列。該問題稱八數碼難題或者重排九宮問題。原始碼 ...