Rush Hour是一款滑板遊戲(這個詞並不陌生,常見的滑板遊戲還有華容道、八數碼等)。規則很簡單,看這裏的圖就明白了。
首先Breadth first search的解法是很容易想到的,但性能不夠理想,而啓發函數也很難設計。Pearls of Functional Algorithm Design裏有一節介紹了一個根據計劃求解的方法。
我們人在求解的時候,一般是這樣想的:“要把0號車移動到終點,要先移開路上的6、7號車。”“要把6號車移開,可以通過移開5號車來實現,也可以移開8號車。”“要移開5號車,要先移開3、4號車。”等等。
求解這個問題時,我們可以讓程序也按照人這樣進行思考:要把0號車移動到終點,途中要依次經過19號格和20號格。19號格被6號車佔據,可以讓6號車依次經過26號格和33號格。26號格被8號車佔據,可以把它移動到23號格來騰出位子……
類似於Depth first search使用棧維護候選狀態,Breadth first search使用隊列維護候選狀態,該算法維護雙端隊列,可能如你所預料的,狀態的擴展方式揉和了
Depth 與 Breadth 兩種方式。
一個狀態不僅要表示棋盤佈局,還要表示一個計劃,計劃中的每個步驟要依次執行。
比如遊戲獲勝的計劃是把0號車移動到19,再把0號車移動到20(注意這兩步有順序),簡記爲(0,19) (0,20)。其中(0,19)的計劃有兩個,只要完成其中一個即可:
(6,26) (6,33)(5,4) (5,3)
其中 (6,26) 的計劃是:
(8,23)
其中 (6,33) 的計劃是:
- ……
……
該搜索算法的初始狀態就是初始棋盤,計劃是
(0,0當前位置右移1格) (0,0當前位置右移2格)
(0,0當前位置右移3格)……直到 (0,出口)。
每次從隊列頭部取出一個狀態 p
進行擴展。其中一種擴展方式和 Breadth
幾乎雷同,把一輛車移動一格,生成的狀態 q
放入隊列尾部。只是要注意 q 的計劃依然是
(0,q中0的位置右移1格) (0,q中0位置右移2格)
(0,q中0位置右移3格)……直到
(0,出口)。也就是說,p
的計劃被完全忽略了。該過程對應代碼中的 bsuccs。
另外一種方式比較麻煩,需要考慮 p 的計劃。
首先要知道計劃是可以 變具體的,也就是說計劃的第一步
s 如果沒法直接達成(即不能通過把一輛車移動一格達到),
那麼這個計劃就可以 具體化。方法是看 s
可以由什麼計劃來達成(比如把另一輛車挪開騰出位子讓 s
對應的車佔據)。當然,這個 具體化
過程可能一步就能完成(只挪開一輛車),也可能需要很多步(要挪開很多車),相當於遞歸展開第一步。
我們要做的就是 具體化 s 使得新計劃
s0 的第一步能夠直接達成,把達成後得到的狀態
s0' 放入隊列頭部。 當然,具體化
的方案可能不止一種,這種情況下我們要考慮所有 具體化 方案
s0 s1 s2……它們對應的轉移
s0' s1'
s2'……要全部放到隊列頭部。該過程對應代碼中的
asuccs。
代碼幾乎抄自 Pearls of Functional Algorithm Design:
1 | {- |