2011-02-15 309 views
6

我已經在Python中實現了一個Rush Hour(拼圖板遊戲)版本,作爲一些AI算法的演示。這個遊戲並不重要,因爲AI相對獨立於它的細節:我試圖在Python中實現一個迭代加深深度優先搜索的版本,如下所示(請注意,該代碼幾乎直接從Russell和Norvig的AI文本複製而來,第三版,對於那些瞭解它的人):迭代深度優化的優化誤差搜索算法

def depth_limited_search(game, limit): 
    node = GameNode(game) 
    frontier = [] 
    #frontier = Queue.Queue() 
    frontier.append(node) 
    #frontier.put(node) 
    frontier_hash_set = set() 
    frontier_hash_set.add(str(node.state)) 
    explored = set() 
    cutoff = False 
    while True: 
     if len(frontier) == 0: 
     #if frontier.empty(): 
      break 
     node = frontier.pop() 
     #node = frontier.get() 
     frontier_hash_set.remove(str(node.state)) 
     explored.add(str(node.state)) 
     if node.cost > limit: 
      cutoff = True 
     else: 
      for action in node.state.get_actions(): 
       child = node.result(action) 
       if str(child.state) not in frontier_hash_set and str(child.state) not in explored: 
        if child.goal_test(): 
         show_solution(child) 
         return child.cost 
        frontier.append(child) 
        #frontier.put(child) 
        frontier_hash_set.add(str(child.state)) 
    if cutoff: 
     return 'Cutoff' 
    else: 
     return None 

def iterative_deepening_search(game): 
    depth = 0 
    while True: 
     result = depth_limited_search(game, depth) 
     if result != 'Cutoff': 
      return result 
     depth += 1 

實施的搜索算法確實會在合理的時間內返回一個答案。問題是答案是非最優的。我選擇的測試板在8次移動中有最佳答案,但是此算法使用10次移動返回一個。如果我用註釋行替換上面註釋過的行的行,有效地將迭代加深的深度優先搜索轉變爲迭代加深的廣度優先搜索,算法會返回最佳答案!

我一直在盯着這個很長一段時間,現在試圖弄清楚遍歷順序中的一個簡單的變化可能會導致非最優化,我無法弄清楚。任何幫助指出我的愚蠢的錯誤將不勝感激

+0

我會看看費用的計算,不包括在這裏,因爲這段代碼看起來一眼就能看出來,畢竟你得到的結果是錯誤的成本值。 – 2011-02-15 08:27:30

+0

啊,對不起,我應該更加明確。節點的「成本」只是達到它的動作數量;由node.result(action)生成的孩子的成本等於node.cost + 1。此外,show_solution()打印出爲達到目標節點而採取的一系列動作,所以看起來似乎不太可能,因爲我可以只需計算步驟。 – Sean 2011-02-15 08:56:10

+1

爲什麼這麼多轉換爲str? – 2011-02-15 11:31:28

回答

2

我不能對此進行測試,但我認爲這個問題是這樣的斷言:

if str(child.state) not in frontier_hash_set and \ 
    str(child.state) not in explored: 

的問題是,先前在本DFS迭代,child.state可能已經被插入到邊境或設置訪問的狀態中,但成本更高

S -> A -> B -> C -> G 
\   /
    \-----> D -/ 

顯然,你不會達到極限與目標< 3.但是,當限= 3,您可以DFS首次訪問A,B和C.然後,當它回溯到S,參觀d,並試圖訪問C,它不會將C推入堆棧,因爲您之前訪問過它。

爲了解決這個問題,我相信你需要在你退路時「取消」訪問狀態。按照實施的方式,遞歸地編寫算法並將功能樣式的探索狀態集的副本傳遞起來可能是最簡單的。

1

你的代碼找到一個次優解決方案的原因很簡單,因爲深度優先搜索和麪包優先搜索工作。

廣度優先搜索會嘗試所有可能的8招解決方案試圖任何 9 - 移動解決方案之前,所以廣度優先搜索必須找到儘可能少的移動解決方案。

相比之下,深度優先搜索會在耗盡所有可能的8移動解決方案之前嘗試一些9,10,11 ...移動解決方案,因此可能會產生次優結果。

例如,給定一個博弈樹,看起來像這樣:

  1 
    /  \ 
    2   3 
/ \  / \ 
4  5  6  7 
/\ /\ /\ /\ 
8 9 A B C D E F 

代碼給出將調用以該順序在節點上goal_test:2,3,6,7,E,F,C ,D,4,5,A,B,8,9。注意,節點E和F在節點6的子節點之前以及節點2的子節點之前被測試。這是深度優先搜索。

您的代碼的替代版本將按以下順序調用goal_test:2,3,4,5,6,7,8,9,A,B,C,D,E,F。優先搜索。

編輯:我的不好,我上面調用goal_test的順序只對最後一次迭代iterative_deepening_search是正確的。對goal_test的調用的實際順序是2,3,2,3,6,7,4,5,2,3,6,7,E,F,C,D,4,5,A,B,8, 9.我通過實際運行代碼驗證了這一點,所以我100%確定它現在是正確的。

您確定child.state的值對於每個遊戲節點都是唯一的嗎?如果它們不是,那麼算法將失敗。例如,考慮節點4與節點6具有相同狀態會發生什麼。在這種情況下,您的代碼將按照以下順序在節點上調用goal_test:2,3,2,3,6,7,5,2,3,6 ,7,E,F,C,D,5,A,B。請注意,goal_test絕不會在節點4,8和9上調用

但是,如果我們切換到代碼的替代版本,則按以下順序調用goal_test:2,3,2,3,4,5,7,2,3,4,5,7,8,9 ,A,B,E,F。現在goal_test不在節點6,C和D上調用!我相信這可能是你的問題的原因。