QS2演算法求解N 皇后問題

2021-06-03 00:44:21 字數 4048 閱讀 4429

這些天逛論壇,忽然發現可以用qs2演算法求解n-皇后問題,而且效果比較好。之前學《人工智慧》曾經用爬山法解決過,但當n上千時,效果很差。論壇裡介紹qs2演算法效果很好。自己便按照樓主給的思路和偽**寫了一遍,果然很厲害啊。在此先讚乙個。(以下藍色部分來自論壇樓主帖子)

8皇后問題是乙個廣為人知的問題:將8個皇后放在8×8的棋盤上,皇后之間不能互相攻擊,求各種放法。更一般的,把8換成n,其解法個數是隨n成幾何級增長的,因此程式執行時間也是幾何級別的。現在我們關注這樣乙個問題,既然不能很快的把所有解都列舉出來,那麼我們能不能很快的求出乙個解來呢?這就是n皇后問題。

有人說,我就用純搜尋來搜第乙個解,會不會快呢?你可以試一試,當n增大的時候會變的非常非常慢。除了搜尋,還能有什麼更好的辦法呢?下面介紹乙個qs2演算法,可以在當n大到500000的時候仍能在幾分鐘之內得到解。描述演算法前,先定義一些資料結構:

1.  狀態表示。用queen[i] (1<=i<=n)儲存1..n個乙個排列,用它表示第i列的皇后所放的行數。這樣表示的作用是,同一行同一列都不會有互相攻擊的情況發生,發生攻擊只可能是對角線。

2.  對角線標記。對於第i行第j列的元素,它有兩個方向的對角線。假設以左下角為座標(1,1)點,那麼一條對角線是斜率為正的,另外一條是斜率為負的。對於斜率為正的對角線上的元素,i-j為定值,對斜率為負的對角線上的元素,i+j為定值,因此用i+j來標記該元素所處的負對角線,用i-j來標記其所處的正對角線。用陣列dn[ ]來表示第i+j條負對角線上有多少個皇后,用dp[ ]來表示第i-j條正對角線上的皇后的個數。定義在某條對角線上,碰撞的個數為這條對角線上皇后的個數減1。

3.  被攻擊的皇后。用乙個陣列attack[ ]來儲存所有被攻擊的皇后的行數。此陣列用於加速程式的執行。 有了這些資料結構,就可以開始介紹qs2演算法了。

演算法如下:

1.  repeat

2.    隨機產生初始狀態queen[i]

3.    collisions = compute_collisions(queen, dn, dp)  (計算每個對角線上皇后的個數,dp和dn,並計算總的碰撞次數collisions)

4.    設limit = c1 * collisions (該變數用於界定程式中每種狀態產生的碰撞次數,c1是乙個引數,這裡設為0.45)

5.    number_of_attacks = compute_attacks(queen, dn, dp, attack)  (計算被攻擊的皇后的個數)

6.    loopcount = 0

7.    repeat

8.      for k <- 1 to number_of_attacks do

9.        i <- attack[k]

10.       隨機選擇乙個1..n之間的數j

11.       if 交換queen[i]和queen[j]可以減少總的碰撞次數collisions then

12.         進行這次交換,並重新計算dn, dp和collisions

13.         if collisions = 0 then 找到解,結束。

14.         if collisions < limit then do

15.           limit <- c1*collisions

16.           重新計算number_of_attacks = compute_attacks(queen, dn, dp, attack)

17.         end do

18.       end do

19.     end do

20.     loopcount <- loopcount + number_of_attacks

21.   until loopcount > c2 * n  (c2是乙個常量,這裡設為32)

22. until collisions = 0

通觀這個演算法,發現其實很簡單,就是先隨機產生了一種排列方案,然後找乙個被攻擊的皇后,隨機選擇另乙個皇后,讓她們交換位置,如果這個交換可以使總的碰撞次數減少,那麼就進行交換。由於這個演算法是隨機的,不能保證從最開始隨機產生的queen[i]狀態一定可以得到一組解,因此,程式在執行一段時間之後,如果沒有解產生,就重新產生一組初始值(這個過程是通過loopcount變數來控制的)。再看看這個演算法的效能。由於程式裡面充滿了隨機化,對這個程式的效能的分析也無法直接從**中來分析其複雜度。它的效能是通過實驗來分析的。下面這個表列出了一些統計數字:

皇后個數n                         1000          10000        100000        500000

被測試的皇后數               14742        138762      1386974     6981193

進行交換的皇后對個數    436            4333           43256         216407

表中第2行被測試的皇后對個數,是指程式第11行對兩個皇后交換後是否可以減少碰撞個數的測試。表中第3行進行交換的皇后對個數,是指程式第12行交換兩個皇后的次數。

從表中我們不難發現,隨著n增大,測試皇后對和交換皇后對這兩個操作都是呈線性增加的。也就是說,對由乙個初始狀態出發到產生解,所用時間是線性的。但是,如果不能從某個隨機的初始狀態產生出解呢?雖說若不能出解,程式執行一段時間後會重新產生初始狀態,但如果不能產生解的初始狀態很多,那麼程式的效能也會大大下降。事實上這個顧慮是多餘的,實驗驗證,當n超過1000的時候,幾乎總可以從第乙個初始值出發找到解!

這個演算法介紹完了,最後,我想說說我對這個演算法的感覺。首先,這個演算法是實驗性的。其效能的評估沒法用傳統的複雜度分析方法來進行,必須通過實驗來驗證。當我看到了這個演算法,如果不是親自去寫一遍,我根本無法相信它會這麼快的出解,這也是它神奇的地方。其次,這個演算法的思路跳出了解決n皇后問題一般所能想到的回溯搜尋方法,取而代之的是乙個隨機化貪心演算法。雖然說它是不確定的,可是它卻快速的解決了問題。這為人們提供了乙個思路,當遇到傳統的搜尋很難奏效的時候,隨機化貪心也許會另闢捷徑,收到意想不到的效果。

最後在貼上自己寫的code

#include #include #include #include using namespace std;

const double c1 = 0.45;

const double c2 = 32;

void initqueen(int n , vector &queen) // 初始化queen

}__int64 compute_collisions(const vector&queen,vector&dn,vector&dp)

for(int i = 0 ; i < (int)queen.size() ; i++)

__int64 collisions = 0;

for(int k =0 ; k < 2*queen.size()-1;k++)

return collisions;

}int compute_attacks(const vector&queen,vector&dn,vector&dp,vector&attack)

} return attackedlines;

}int randint(int k ,int size)

int cacl_collisions(const vector&queen,vector&dn,vector&dp,int a,int b)

void updatequeen(vector&queen,vector&dn,vector&dp,int a,int b)

void printqueen(vector&queen)

coutvectorattack;

vectordn(2*n);

vectordp(2*n);

if(n == 2||n == 3)

{ cout<<"無解"<>n;

srand((unsigned)time(null));

starttime = clock();

pashan(n);

endtime = clock();

double totaltime = 1.0*(endtime - starttime)/clocks_per_sec;

cout<<"共耗時:"<

求解n皇后問題

n皇后問題 回溯法 遞迴的邏輯中一般有兩個重要的概念 1.遞迴邊界 2.遞迴式 1.問題描述 在n n格的西洋棋上擺放n個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行 同一列或同一斜線上,問有多少種擺法。遞迴思想 int count 0 void generatep int index i...

n皇后問題求解

在乙個n n的棋盤上放置n個王后,使得每個王后不會相互攻擊,即任意兩個王后不在同一行 同一列 同一對角線上,輸出所有的放置方式。輸入n,表示棋盤大小。深度優先搜尋 dfs 列舉第i個王后的放法,再列舉第i 1個王后的放置方法,直至放置完所有棋子,檢查放置方式是否合法,若合法則輸出 不合法則返回,嘗試...

使用C 求解N皇后問題。

下面這個演算法實現了列出所有可能放置的情況。using system namespace queen 顯示當前矩陣情況。public void display console.writeline 判斷當前位置是否可以放置皇后。private bool iscurrentcanplaced int r...