上兩篇博客html
研究了遞歸方法實現回溯,解決N皇后問題,下面咱們來探討一下非遞歸方案java
實驗結果使人仍是有些失望,原來非遞歸方案的性能並不比遞歸方案性能高算法
代碼以下:安全
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; } }
最終的執行效率爲:性能
這跟咱們第一篇博客的遞歸調用的效率:spa
仍是有些差距,因此算法屆大張旗鼓的所謂「遞歸影響性能」的說法並不存在,至少在這個問題上有待探討線程
最後我還想再實現如下多線程解決N皇后的問題
由於我發現不管用不用遞歸,個人N皇后程序跑起來的時候,CPU使用率都在15%如下
可能用了JAVA的緣故,虛擬機沙盒有限制,並且是多核的CPU,暫時也沒搞明白爲何不能發揮更高的CPU使用率
最後我將用多線程再次嘗試更高的程序性能,看看可否有突破。