1. 程式人生 > >使用Python生成器解決八皇后問題

使用Python生成器解決八皇后問題

像我這種對奇特的語言特性很熱衷,對演算法本身並不熱衷也並不擅長的人,似乎很難去寫一篇關於演算法題的文章。好在我還有一個特點就是不管寫了多麼爛的程式碼都會拿出來得瑟一下,不怕被拍磚,於是便來分享一下關於使用Python中的yield來解決八皇后問題的心得。

Python生成器

PEP 255中詳細介紹了Python Generator。簡單來說它可以將一個函式當作可以迭代的物件來使用,此舉真是將高階函式的特性發揮得淋漓盡致(Java什麼的一邊玩去吧)。一個很簡單的例子:

def fib():
    a, b = 0, 1
    while 1:
        yield b
        a, b = b, a+b

這能夠產生一個無窮盡的菲波納契數列,可以使用for死迴圈般地遍歷它:
for i in fib():
    print i
yield關鍵字的要訣就是替換return,但是它只是中途返回一個值,而不是將整個函式返回,第二次呼叫這個函式的時候,會從yield之後的地方繼續執行。聽上去跟閉包(Closures)頗類似呢。

八皇后問題

這其實是個討論得很濫的問題了,所以人們一般稱它為『經典問題』。大家知道國際象棋是8×8的棋盤,其中的“皇后”可以橫豎斜四通八達地遊走和吃子。八皇后問題就是怎麼將八個皇后放在棋盤中,讓它們互相不能吃掉。這個問題有92個互不相同的解,如果把旋轉和對稱的解算做同一種,那便是12個獨立解。

暴力解決法



很容易看出來,在每一行和每一列上有且只能有一個皇后,因此較為複雜的判斷就是對角線了。維基百科的頁面上有一個非常暴力但是寫起來非常簡單的解法:

from itertools import permutations

n = 8
cols = range(n)
for vec in permutations(cols):
    if (n == len(set(vec[i] + i for i in cols))
          == len(set(vec[i] - i for i in cols))):
        print vec
cols是一個長度為8的陣列,其索引代表第幾行,值代表第幾列,於是便可以用這個列表來表示8個皇后的座標了。這個暴力解法將所有可能進行全排列後一個一個地判斷是否有對角線共線,所以是非常慢的。

稍微不那麼暴力的方法


終於到了本文的主題了。稍微不那麼暴力的方法就是,一個一個計算列的值,每次的可用值都是依賴於前面已經計算好了的位置的,如果一直執行到最後都有可選擇的位置,那麼自然就成功了。這樣的方法用Generator來實現真是天作之合。
def next_col(current, n=8):
    length = len(current)
    if length == n:
        return
    dangerous = current + [item for l in [(val + length - i, val - length + i)
                    for i, val in enumerate(current)]
                           for item in l if item >= 0 and item <= n]
    for i in range(n):
        if i not in dangerous:
            yield i

def queens(n=8, columns=[]):
    if len(columns) == n:
        yield columns
    for i in next_col(columns, n):
        appended = columns + [i]
        for c in queens(n, appended):
            yield c

def prettyprint(solution):
    def line(pos,lengh=len(solution)):
        return '.'*(pos)+'X'+'.'*(lengh-pos-1)
    for pos in solution:
        print(line(pos))

if __name__ == '__main__':
    i = 0
    for solution in queens(8):
        # print(i, solution)
        i+=1
        if i > 1: print '\n'
        print '第' + str(i) + '解'
        prettyprint(solution)
"""
.......X
...X....
X.......
..X.....
.....X..
.X......
......X.
....X...
"""

原文:http://blog.dayanjia.com/2012/10/solve-n-queen-puzzle-using-python-generator/