上兩篇博客
8皇后以及N皇后算法探究,回溯算法的JAVA實現,遞歸方案
8皇后以及N皇后算法探究,回溯算法的JAVA實現,非遞歸,數據結構“棧”實現
研究了遞歸方法實現回溯,解決N皇后問題,下面我們來探討一下非遞歸方案
實驗結果令人還是有些失望,原來非遞歸方案的性能並不比遞歸方案性能高
代碼如下:
package com.newflypig.eightqueen; import java.util.Date; /** * 使用循環控制來實現回溯,解決N皇后 * @author newflydd@189.cn * Time : 2016年1月1日 下午9:37:32 */ public class EightQueen4 { private static short K=15; private static short N=0; private static boolean dead=false; //下方走到了死路 public static void main(String[] args) { for (N = 9; N <= K; N++) { Date begin = new Date(); dead=false; long count = 0; /** * -2:初始狀態,尚未擺放 -1:開始嘗試擺放 0到N-1:皇后安全的擺放在這一列的哪一行 */ short[] chess = new short[N]; for (short i = 1; i < N; i++) chess[i] = -2; OUT: while (chess[0] != -2) { if (dead) { /** * 如果下方的皇后已經擺無可擺,已經走到死路 則要將當前最后一個安全的皇后右移 右移成功后,判斷安全性 * 安全:dead清除,繼續外部循環 不安全,則繼續右移,直至邊界溢出,再次死路 */ while (moveStep(chess)) { if (isSafety(chess)) { dead = false; continue OUT; } } } else { /** * 如果當前狀態下的安全棋盤並沒有接受到下方傳來的死路信號 則需要進一步探測下一行的擺放位置 */ short row = getRow(chess); chess[row + 1] = -1; // 准備對下一層擺放皇后 while (moveStep(chess)) { if (isSafety(chess)) { if (row + 1 == N - 1) { // 如果最后一行找到了一個可能解 count++; // 計數+1 /** * 找到解以后,dead設為死路,最后一行清掉皇后,同時倒數第二行也要清掉皇后 */ dead = true; chess[N - 1] = -2; continue OUT; } continue OUT; } } } } Date end = new Date(); System.out.println("解決 " + N + "皇后問題,用時:" + String.valueOf(end.getTime() - begin.getTime()) + "毫秒,計算結果:" + count); } } private static boolean moveStep(short[] chess) { short row=getRow(chess); if(chess[row]+1>=N){ /** * 擺到邊界,清空當前行的擺放記錄,標志死路 */ chess[row]=-2; dead=true; return false; } chess[row]=(short) (chess[row]+1); return true; } private static short getRow(short[] chess) { short row=(short) (N-1); while(chess[row]==-2){ row--; } return row; } private static boolean isSafety(short[] chess) { short row=getRow(chess); short col=chess[row]; //判斷中上、左上、右上是否安全 short step=1; for(short i=(short) (row-1);i>=0;i--){ if(chess[i]==col) //中上 return false; if(chess[i]==col-step) //左上 return false; if(chess[i]==col+step) //右上 return false; step++; } return true; } }
程序中定義了全局變量dead死路標志,告訴循環什么時候需要回溯,什么時候需要繼續深搜
getRow() 函數返回當前最后擺放皇后的行號,每次擺放皇后和判斷安全性時都要調用,所以顯得性能偏低
下面取消了getRow()函數,使用全局變量row來表示已經擺到那一行的皇后了,用一個小小的變量空間換了一部分時間:
package com.newflypig.eightqueen; import java.util.Date; /** * 使用循環控制來實現回溯,解決N皇后 * 開辟兩個變量控制行和列,避免不必要的計算,空間換時間 * @author newflydd@189.cn * Time : 2016年1月1日 下午9:37:32 */ public class EightQueen5 { private static short K=15; private static short N=0; private static boolean dead=false; //下方走到了死路 private static short row=0; public static void main(String[] args) { for (N = 9; N <= K; N++) { Date begin = new Date(); row=0; dead=false; long count = 0; /** * -2:初始狀態,尚未擺放 -1:開始嘗試擺放 0到N-1:皇后安全的擺放在這一列的哪一行 */ short[] chess = new short[N]; for (short i = 1; i < N; i++) chess[i] = -2; OUT: while (chess[0] != -2) { if (dead) { /** * 如果下方的皇后已經擺無可擺,已經走到死路 則要將當前最后一個安全的皇后右移 右移成功后,判斷安全性 * 安全:dead清除,繼續外部循環 不安全,則繼續右移,直至邊界溢出,再次死路 */ while (moveStep(chess)) { if (isSafety(chess)) { dead = false; continue OUT; } } } else { /** * 如果當前狀態下的安全棋盤並沒有接受到下方傳來的死路信號 則需要進一步探測下一行的擺放位置 */ chess[++row] = -1; // 准備對下一層擺放皇后 while (moveStep(chess)) { if (isSafety(chess)) { if (row == N - 1) { // 如果最后一行找到了一個可能解 count++; // 計數+1 /** * 找到解以后,dead設為死路,最后一行清掉皇后 */ dead = true; chess[N - 1] = -2; row--; continue OUT; } continue OUT; } } } } Date end = new Date(); System.out.println("解決 " + N + "皇后問題,用時:" + String.valueOf(end.getTime() - begin.getTime()) + "毫秒,計算結果:" + count); } } private static boolean moveStep(short[] chess) { if(chess[row]+1>=N){ /** * 擺到邊界,清空當前行的擺放記錄,標志死路 */ chess[row]=-2; row--; dead=true; return false; } chess[row]=(short) (chess[row]+1); return true; } private static boolean isSafety(short[] chess) { short col=chess[row]; //判斷中上、左上、右上是否安全 short step=1; for(short i=(short) (row-1);i>=0;i--){ if(chess[i]==col) //中上 return false; if(chess[i]==col-step) //左上 return false; if(chess[i]==col+step) //右上 return false; step++; } return true; } }
最終的執行效率為:
這跟我們第一篇博客的遞歸調用的效率:
還是有些差距,所以算法屆大張旗鼓的所謂“遞歸影響性能”的說法並不存在,至少在這個問題上有待探討
最后我還想再實現以下多線程解決N皇后的問題
因為我發現無論用不用遞歸,我的N皇后程序跑起來的時候,CPU使用率都在15%以下
可能用了JAVA的緣故,虛擬機沙盒有限制,而且是多核的CPU,暫時也沒搞明白為什么不能發揮更高的CPU使用率
最后我將用多線程再次嘗試更高的程序性能,看看能否有突破。