# Bitwise N-Queens ###### tags: `C LANGUAGE` :::success Bitwise Operator 一直是自己不太熟悉的一個部份,藉由閱讀`jgpettibone` 的 Bitwise N-Queens,並且將其內容做成筆記方便日後複習。 ::: [jgpettibone的文章](http://jgpettibone.github.io/bitwise-n-queens) --- ## N-Queens N-Queens 這個問題早是由 8-Queens 問題延伸而來。在西洋棋的規則中 Queen 能夠吃掉在同 Column 跟同 Row 還有在其左右對角線的棋子。 而 8-Queees 問題就是在,8x8 的棋盤上有多少解是能夠讓8個皇后同時生存,而這個問題是 Backtracking 演算法的一個相當經典的應用。 #### BackingTracking (回溯法) 簡單介紹就是在無窮枚舉遞迴的過程中,在遞迴下去前檢查答案是否是自己要的,如果不是就不在往下跑,這樣就能減少所多無意義的運算,有點類似 Branch and Bound。 ### Bitwise Implement 接下來就直接進入主題,看看 `jgpettibone` 是如何 Implement。 #### Board Position 取代利用陣列來表示 Queen 的位置,這邊利用二進位的1來表示目前的位置, 例如如果是一個4x4的棋盤,我們用1、2、4、8的二進位來表示位置,然後在用 Left shift `<<`來作 Queen 位置的操作。 :::info 0001 1 這代表 Queen 目前在最後一位。 0010 2 0100 4 1000 8 這樣的方法,可以利用 `AND` 來判斷 Queen 是否有在同 Column 的狀況。 ::: #### Conflicts 處理 因為我們是用二的倍數來操作所以不用擔心 Row 是否有 Conflict 的問題,要注意的是 Column 跟左右對角線。 :::success 0010 目前 Queen 要放的位置 0100 Queen 跟右下角衝突 0001 與下面對角線衝突 0010 與第一個 Column 衝突 ::: 從上面的例子可以看到`0010`這個位置有兩種 Conflict 分別是與右下角衝突跟 Column 衝突,那要怎找到一個適合的位置? :::success 0100 0001 1000 一一一 1101 ::: 可以發現原本的 Queens 存在的位置做過 `OR` operator 之後,會得到1101,這個時候從簡單的4x4中可以觀察到在0010是唯一能夠放 Queen 的地方,如果我們將1101跟0010做 `AND` operator 之後。 :::success 1101 0010 一一一 0000 ::: 可以得到0000,那就代表已存在的所有 Queens 做 `OR` 後得到的值再跟欲放入 Queen 的位置做 `AND` 如果得到的值都為0就代表此位置是可行。 #### 實際作法 Step One: 初始化三個變數分別代表左對角線、右對角線、Column 會遇到 Conflict 的位置。 Step Two: 將這三個變數做 OR operator 之後,就可以利用這個去對新的位置用 `AND` operator 判斷是否有 Conflict。 Step Three: 根據新進來的Row來變更那三個變數。 :::success 左對角線 0001 上一個左對角線參數 1000 現在進來的 Row 一一一 1001 作 OR operation 0100(1) 然後向右 shift 一個 bit 這樣就可以得到下一個 Row 進來是否有 Conflict 的判斷。 ::: :::info 右對角線 0100 上一個右對角線參數 1000 現在進來的 Row 一一一 1100 作 OR operation (1)1000 然後向左 shift 一個 bit 這樣就可以得到下一個 Row 進來是否有 Conflict 的判斷。 ::: :::danger Column 0010 上一個 Column 參數 1000 現在進來的 Row 一一一 1010 作 OR operation 這樣就可以得到下一個 Row 進來是否有 Conflict 的判斷。 ::: 以上就是 Bitwise N-Queens 簡單的方法介紹,以下會附上 Implement 的 Code ```clike #include <stdio.h> #include <stdlib.h> //this struct record the conflict information typedef struct quene_propety { int minDiagnoalConflict; int maxDiagnoalConflictm; int ColConflict; int n_number; } quene_pro; void quene_count(int current_row ,quene_pro *quene, int *solution_count) { //the current initial is zero //if the current row past the n_number mean find the one solution //solution_count plus one which mean the solution number if (current_row == quene->n_number){ (*solution_count)++; free(quene); return; } int conflicts = quene->maxDiagnoalConflictm | quene->minDiagnoalConflict | quene->ColConflict; // first iteration is 1 and multiple 2 to the next bit // if n = 4 , (1 << 4) = 10000 is 16 so the most left bit is 1000 = 8 // the quene position = 1000 [0100 = 4 0010 = 2 0001 = 1] for (int quene_position = 1; quene_position < (1 << quene->n_number); quene_position *= 2){ if (!(conflicts & quene_position)){ quene_pro *q_new = malloc(sizeof(quene_pro)); q_new->maxDiagnoalConflictm = (quene->maxDiagnoalConflictm | quene_position) >> 1; q_new->minDiagnoalConflict = (quene->minDiagnoalConflict | quene_position) << 1; q_new->ColConflict = quene->ColConflict | quene_position; q_new->n_number = quene->n_number; quene_count(current_row + 1 ,q_new, solution_count); } } } int quene_cross(int *number) { int solution_count = 0; quene_pro p = {0, 0, 0, *number}; quene_count(0, &p, &solution_count); return solution_count; } int main() { int n_number = 0; int counts = 0; scanf("%d", &n_number); counts = quene_cross(&n_number); printf("%d Solutions\n", counts); return 0; } ```