1. 程式人生 > >爬山法實現 八皇后問題 (Python 實現)

爬山法實現 八皇后問題 (Python 實現)

本文主要簡單闡述爬山法的基本演算法思想,並給出用此演算法實現八皇后問題詳細過程

最基本的爬上搜索演算法表示:(節選自《人工智慧》第二版):

function HILL-CLIMBING(problemreturn a state thate is a locak maximum
    inputsproblem
    local variablescurrent, a node 
                         neighbor,a node
    current = MakeNode(INITAL-STATE(problem));
    loop do


        neighbor = a highest-valued successor of current ;
        if VALUE[neighbor] <= VALUE[currentthen return STATE[current];
        current neighbor ;

演算法特點:

        爬山法是一個向值增加的方向持續移動的簡單迴圈過程--類似於登高,它將會在到達一個“峰頂”時終止,此時相鄰狀態中沒有比它更高的值。這個演算法不會維護搜尋樹,因此當前節點的資料結構只需要記錄當前狀態和它的目標函式值,它不會前瞻與當前狀態不直接相鄰的那些狀態的值。這裡更高的值是根據具體的應用場景來定的,準確的說是更接近目標狀態的值,由我們設定的啟發式函式來決定哪個值更好,比如我們求解八皇后問題更高的值就是當前格局中皇后衝突對數最少的情況

演算法侷限性:

      爬山法屬於一種區域性的貪婪搜尋方法,當它在搜尋過程中遇到了區域性最大值就很難繼續向下搜尋了區域性極大值是一個比它的每個鄰居狀態都高得到峰頂,但是比全域性最大值(我們要到達的最終狀態)要低,爬山法演算法到達區域性極大值附近就會被拉向峰頂,然後卡在區域性極大值處無處可走。因此產生了爬山法的變種如隨機爬山法及其變種如隨機爬山法,隨機重新開始的爬山法,模擬退火搜尋能夠非常有效的解決N皇后問題。

求解八皇后問題:

#-*- coding: utf-8 -*- 

import random
#函式一:引數為當前棋盤佈局狀態,根據佈局判斷當前八皇后佈局存在衝突的皇后對數
def get_numof_conflict(status):
        num = 0
        for i in range(len(status)):
                for j in range(i + 1,len(status)):

                        if status[i] == status[j]:
                                num += 1
                        offset = j - i
                        if abs(status[i]-status[j]) == offset:
                                num += 1
        return num
#函式二:引數為當前棋盤佈局狀態,利用爬山法思想選擇鄰居狀態最好的佈局並返回
def  hill_climbing(status):
        convert = {}
        length = len(status)
        for col in range(length):
                best_move = status[col]
                for row in range(length):
                        if status[col] == row:
                                continue
                        status_copy = list(status)
                        status_copy[col] = row
                        convert[(col,row)] = get_numof_conflict(status_copy)

        answers = [] #最佳後繼集合
        conflict_now = get_numof_conflict(status) #當前皇后衝突對數

        #遍歷儲存所有可能後繼的字典,找出最佳後繼
        for key,value in convert.iteritems():
                if value < conflict_now:
                        conflict_now = value
        for key,value in convert.iteritems():
                if value == conflict_now:
                        answers.append(key)

        #如果最佳後繼集合元素大於一個 隨機選擇一個
        if len(answers) > 0:
                x = random.randint(0,len(answers)-1)
                col = answers[x][0]
                row = answers[x][1]
                status[col] = row

        return status

#函式三:求得八皇后滿足衝突數為0的一個解,迴圈輸出每一步的後繼集合 直到不存在衝>突為止
def Queens():
        status = [0,1,2,3,4,5,6,7] #初始狀態所有皇后都在對角線

        #當存在衝突的個數大於0時 迴圈求解最佳後繼 直到找到八皇后解
        while get_numof_conflict(status) > 0:
                status = hill_climbing(status)
                print status
                print get_numof_conflict(status)
        print "the answer is"
        print
        print status

if __name__ == '__main__':
        Queens()

程式碼說明 :

(1)啟發式耗散函式 get_numof_conflict求出可以彼此攻擊2的皇后對的數  hill_climbing是演算法中爬山過程的實現,convert字典中儲存的鍵是所有可能後繼狀態,值是此狀態對應的皇后衝突對數,後面通過遍歷來找到最佳後繼。爬山法演算法通常在最佳後繼集合中隨機選擇一個進行擴充套件,如果這樣的後繼多於一個的話

(2)  棋盤的排列用列表 status 表示, 依次表示第一列第二列到第n列的擺放位置。 如列表[0,2,1] 表示3*3矩陣第一列皇后放在最下面一格,第二列放在最上面一格,第三列放在中間一格

(3) 執行結果說明:初始狀態每個皇后都在次對角線上 每次執行程式將輸出求解一個八皇后正確解的爬山法實現過程 ,依次輸出求解過程每一個最佳選擇的鄰居狀態和其存在的皇后衝突對數,可以看出在求解過程中 不斷爬山的過程--每進行一步都達到一個更好的格局,衝突次數更少,最後一行顯示的是八皇后的一個正確解 衝突數為 0

執行截圖