八皇后問題是一個古老而又著名的問題,是學習回溯算法的一個經典案例。今天咱們就一塊兒來探究一下吧!算法
時間退回到1848年,國際西洋棋棋手馬克斯·貝瑟爾提出了這樣的一個問題,編程
在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問一共有多少種擺法。
後面陸續有不一樣的學者提出本身的看法。大數學家高斯認爲一共有76種擺法,1854年在柏林的象棋雜誌上不一樣的做者發表了共計40種不一樣的看法,後來還有人利用圖論的方法得出共有92種擺法。segmentfault
而現在,經過咱們的計算機以及編程語言咱們能夠輕鬆的解決這個問題。數組
最直接的也是最容易想到的一種解法即是暴力法,咱們能夠在8×8的格子中任選8個皇后,選定後看是否知足任意兩個皇后都不處於同行同列同斜線的條件,若知足則累計知足條件的方案。學習過排列組合的咱們發現64取8這個數字達到了40億,顯然是使人難以接受的。編程語言
但咱們根據這個條件,咱們能夠人爲地作出一些選擇,好比根據條件咱們可知每行每列最多都只能有一個皇后,這樣能夠在必定程度上縮減問題的規模。在第一行的某一列選擇放置一個皇后,共有8種不一樣的選擇,而第二行只能選擇剩下的7列,也就是7種選擇,剩下每一行的選擇都會遞減1,那麼總共可供選擇的方案有8的階乘種,已是一種遠優於暴力解法的解法,可是這個階乘的時間複雜度恐怕也難以使人接受,還有更優的解法嗎?函數
那是天然的,這即是遞歸回溯的方法。學習
當咱們選擇了第一個皇后的位置以後,與其處於同行同列同斜線的位置便都沒法被選擇,第二個皇后只能放在未被第一個皇后所輻射到的位置上,接着放置第三個皇后,一樣不能放在被前兩個皇后輻射到的位置上,若此時已經沒有未被輻射的位置可以被選擇,也就意味着這種擺法是不可行的,咱們須要回退到上一步,給第二個皇后從新選擇一個未被第一個皇后輻射的位置,再來看是否有第三個皇后能夠擺放的位置,如仍是沒有則再次回退至選擇第二個皇后的位置,若第二個皇后也沒有更多的選擇則回退到第一個皇后,從新進行位置的選擇。spa
總體的方法便如上所述,下面用直觀的代碼來實現這個算法,code
def find_Queen(row): if row>7: global count count+=1 print_queen() return for column in range(8): if check(row,column): Queen[row][column]=1 find_Queen(row+1) Queen[row][column]=0
定義一個二維數組Queen,數組中相應位置爲1則表示該位置放置皇后,按行來擺放皇后的位置,若是當前選擇無法繼續往下找到皇后的放置位置,則將以前置爲1的從新置爲0,也就是回退。而check函數的主要目的是爲了篩選皇后的合適位置以知足條件。具體能夠分爲三塊,行列檢查,主對角線以及負對角線檢查。blog
def check(row,column): # 檢查行列 for k in range(8): if Queen[k][column]==1: return False # 檢查主對角線 for i,j in zip(range(row-1,-1,-1),range(column-1,-1,-1)): if Queen[i][j]==1: return False # 檢查副對角線 for i,j in zip(range(row-1,-1,-1),range(column+1,8)): if Queen[i][j]==1: return False return True
當已經放置了八個皇后時,進入 if 語句,累計數值而且打印出相應的皇后擺放示意圖。
實體的星星表示當前位置擺放了皇后,而具體的打印代碼以下所示,
def print_queen(): print(Queen) for i in range(8): for j in range(8): if Queen[i][j]==1: print('☆ '*j+'★ '+'☆ '*(7-j)) print("\n\n")
這樣,經過遞歸回溯的辦法,咱們找到了八皇后的92種解,而且以形式化的方法打印了出來。經過對八皇后問題的學習,咱們能夠深入體會到回溯的思想~