八數碼問題,簡單地來描述是這樣的:在乙個九宮格內,填有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 八個數碼 每個數碼佔一格 且有乙個空格。這些數碼可以在棋盤上移動,其移動規則是 與空格相鄰的數碼方格可以移入空格。現在的問題是 對於指定的初始棋局和目標棋局,給出數碼的移動序列。該問題稱八數碼難題或者重排九宮問題。原始碼 ...