1. 程式人生 > >【八皇后問題】善用數學規律提升演算法效能

【八皇后問題】善用數學規律提升演算法效能

或許很多讀者尚未發現,八皇后問題存在一個重要數學規律。如何運用這個規律對演算法進行效能優化,使得約束函式的時間複雜度從O(n)降至O(1),相信是每一個演算法愛好者所關心的。今天寫這篇文章的目的便是帶大家一起探究一下,八皇后問題之中到底存在什麼樣的數學規律,能夠擺脫傳統的遞迴解法,使得效能提升一個量級。

先放出基本遞迴解法的程式碼吧!

public int caculate(int[] locationRecord, int n, int curHang, int totalCount) {
        for (int i = 0; i < n; i++) {
            locationRecord[curHang] = i;
            //檢查是否和上面已存在的皇后衝突
            boolean hasConflict = false;
            for (int j = 0; j < curHang; j++) {
                if (locationRecord[j] == locationRecord[curHang] || locationRecord[j] - locationRecord[curHang] == curHang - j || locationRecord[curHang] - locationRecord[j] == curHang - j) {
                    hasConflict = true;
                    break;
                }
            }
            if (hasConflict == false) {
                int nextHang = curHang + 1;
                if (nextHang == n) {
                    totalCount++;
                } else {
                    totalCount = caculate(locationRecord, n, nextHang, totalCount);
                }
            }
        }
        return totalCount;
    }

想必大部分讀者都掌握了基本的遞迴解法,這裡大致介紹一下吧。首先強調一下規則:棋盤中,同一行,同一列,同一條對角線上不能同時存在兩個皇后。按照規則,我們可以選擇按行來遍歷,每一行放置一個,也可以選擇按列來遍歷,每一列放置一個。上面解法使用按行遍歷,從第一行遍歷至第八行,每一行找一個合法位置放置皇后。當第八行(最後一行)成功放置皇后之後,便找到一個解,然後return回溯,繼續找下一個解。

上述程式碼中,在每一行元素遍歷中,都會檢查是否存在衝突,這個檢查也成為約束函式。檢查的方式很簡單,就是開一個for迴圈,判斷當前行的當前元素是否和當前行之上的任一一行中皇后存在衝突,如果衝突就繼續遍歷下一個元素,如果不衝突就在當前元素位置放置皇后,並繼續遍歷下一行。這個約束函式的實現很簡單,也很直觀易懂。但是卻造成了O(n)的複雜度,未免有點讓人難以接受。

怎麼去優化約束函式呢?我們首先來看一下存在衝突時的情況。

0 1 2 3 4 5 6 7
0 X1
1 X2
2 X7 X3
3 X8 X4
4 X9 X5 X10
5 X6 X11
6 X12
7

現在我們來做出一波分析。X1,X2,X3之間互相沖突,X1座標為(0,1),X2座標為(1,2),X3座標為(2,3),觀察可發現一個規律,它們在同一條主對角線上,並且縱座標減去橫座標值恆為1。同樣的,在同一條主對角線上的X7,X8,X9也存在相互衝突,X7座標(2,1),X8座標(3,2),X9座標(4,3),它們的縱座標減去橫座標值恆為-1。到這裡,我們已經發現並確定這個規律了,同一對角線上的元素,它們的橫縱座標差值是相等的。

不不,這話說的太早,哈哈!是不是忽略副對角線的情況了,好的,我們再分析下副對角線的情況。X4,X5,X6在同一副對角線並且相互衝突,X4座標(3,7,),X5座標(4,6),X6座標(5,5),仔細觀察可得知,它們的橫座標值加縱座標值之和恆為10,X10,X11,X12亦是這個規律。

好的,現在規律已經找出來了,後面就是怎麼去程式設計實現啦。

同一對角線的元素既然橫縱座標差值相等,那麼我們就可以通過一個布林陣列來標標識某條對角線是否存在皇后,設這個陣列為v[16],比如某個元素位置放置了皇后,則在陣列v中以這個元素橫縱座標差值為下標的地方,設定值為true,之後遍歷到該對角線上其他元素時,只需檢查一下陣列中以橫縱座標差值為下標處的值為不為true就行,為true則視為存在衝突,當前元素位置不可取。這個檢查只需一步操作就行,也就是O(1)的複雜度。

是不是很激動,運用一個數學小規律,就把O(n)的複雜段降為了O(1)。

好了,八皇后問題規律總結到這裡就結束啦,大夥兒可以操刀寫一波程式碼了!順便看下耗時是不是縮短了,嘿嘿!