再探馬周遊問題

【轉載】買回了王曉東的《算法設計與分析習題解答》,書中代碼是用Java寫的,看了跳馬問題的部分,基本理解了算法。首先說明一下,《算法設計與分析》原書的題目其實是要找一條哈密爾頓通路,而《習題解答》中是解哈密爾頓迴路的,即不僅要不重複的跳過棋盤的每一個格子,最後還要能回到出發點。先解釋一下尋找哈密爾頓迴路的算法:
【問題描述】
對於給定的m × n的國際象棋棋盤,mn均爲大於5的偶數,且|m - n| <= 2,試設計一個分治算法找出一條馬的哈密爾頓迴路。
【算法】
首先,考慮n × n的棋盤,馬踏棋盤是黑白相間的,對於一條哈密爾頓迴路來說,馬在棋盤上所踏過的黑色格子和白色格子相等,因此,棋盤的格子數應爲偶數,n不能爲奇數。即n爲奇數的n × n棋盤不存在哈密爾頓迴路(但可能存在哈密爾頓通路)。而對於m × n的棋盤(m != n),m, n均爲奇數則不存在哈密爾頓迴路。好在題目已經限定m, n均爲偶數且|m - n| <= 2,這說明在這種情況下一定存在哈密爾頓迴路。而在其他情況下,如m, n一奇一偶或|m - n| > 2是否存在哈密爾頓迴路則不好說。至於題目爲何要做此限定,如何證明這種限定的合理性則有待探究,當然對此問題的討論也超過了本文的範圍。
現在回到題目本身,算法考察一類具有特殊結構的解,這類解在棋盤的4個角都包含2條特殊邊,如下圖。稱具有這類特殊結構的哈密爾頓迴路爲結構化的哈密爾頓迴路。

用回溯法可在O(1)(留有疑問)時間內找出6 × 6, 6 × 8, 8 × 8, 8 × 10, 10 × 10, 10 × 12棋盤上的結構化的哈密爾頓迴路。同時注意到四個角上每個點只有兩條邊,而哈密爾頓迴路要經過這個角,走的邊不能重複,因此這兩條邊必然在漢密爾頓迴路的路徑上。故結構化的解一定形如下圖:

而對於6 × 8, 8 × 10, 10 × 12的棋盤,旋轉90度即可得到8 × 6, 10 × 8, 12 × 10的棋盤上結構化的哈密爾頓迴路。
對於m, n >= 12的情形,採用分治策略。
1. 分治:將棋盤儘可能的平均分割成4塊,當m, n = 4k時,分割爲22k;當m, n = 4k + 2時,分割爲12k12k + 2
2. 合併:4個子棋盤拼接後如下圖:



分別刪除4個子棋盤中的結構化的邊A, B, C, D,添入新的邊E, F, G, H,構成整個棋盤結構化的哈密爾頓迴路,如下圖所示:
                
從圖中可以看出, 合併的過程其實很簡單,如果把A, D, C, B看作風扇的槳葉,則合併後就是將槳葉依次逆時針向下打了一個點(逆時針旋轉)。
至此,算法結束,我們可以看到,當n >= 12是使用分治法,分成4個子棋盤,而合併過程只需常數時間即可完成,設該算法時間爲T(n),則有遞推方程:T(n) = 4T(n / 2) + O(1),據Master定理知T(n) = O(n ^ 2),這就是在棋盤中尋找哈密爾頓迴路的時間複雜度。比《跳馬問題(騎士周遊問題)初探 》一文中從給定起始點尋找哈密爾頓通路的問題快了不少。
【分析】
回到《算法分析與設計》一書中課後的那道題,是否存在一個分治的求哈密爾頓通路的算法呢?這個問題我現在還沒有確切的答案,因爲從網上搜索的一些資料看(CSDN的帖子中很多人問了這個問題),有幾個網友說有一篇論文似乎是論述了類似的問題,分治的方法十分複雜,要根據不同的情況去分割問題,但很遺憾,這些網友們給出的論文地址已經無效,我沒有看到確切的方法。
但如果想用類似解哈密爾頓迴路的算法來解決哈密爾頓通路的問題,我分析認爲不可能(即要分治不可能是像解迴路一樣分治,如前所說,答案可能在那篇神祕的論文中)。這種不可能正是由於“迴路”和“通路”這一字之差引起的:迴路與起始點無關,算法只需在棋盤中能夠找到一條哈密爾頓迴路,則從任何一點都能不重複的經過棋盤上每一點並回到起始點。而哈密爾頓通路與起始點有關,且路徑不是封閉的,這就使得迴路中考察的結構化路徑的解可能不存在,考慮起始點在最左上角的情形,通路不用再回到這個左上角,所以角上的兩條邊只用一條,這樣在合併時通路問題就不會出現結構化路徑中的中心對稱結構。 因此,我們不能從迴路算法中得到啓發去找到通路問題的分治解。
當然,由於通路是比迴路弱的一個解要求,當棋盤滿足m, n爲偶數,m, n >= 6且|m - n| <= 2的條件時,我們可以直接調用這個迴路算法得到一條哈密爾頓迴路,然後刪去起始點和終點的邊即得通路的解,而在不滿足這個條件時,如5 × 5的棋盤上,對某些起始點是存在哈密爾頓通路的,仍然需要使用回溯的算法去求解,時間複雜度爲O(8 ^ (m * n))。
從回溯算法的時間複雜度,又引出前面書中所說:“用回溯法可在O(1)時間內找出6 × 6, 6 × 8, 8 × 8, 8 × 10, 10 × 10, 10 × 12棋盤上的結構化的哈密爾頓迴路。”的疑問,回溯法怎麼可能是在O(1)時間內得到這些棋盤上回路的解呢?如果這樣,就要求在這些規模的棋盤上構建議一條結構化的迴路(即四角對稱的迴路)有固定的跳馬方法,這種方法使得每一步跳馬時的步子只有唯一選擇,但即使這樣也至少是個線性時間的複雜度啊。從書中的源代碼看,好像是直接一一的讀取這些規模棋盤上的迴路(即迴路已經手工構造好了),這樣複雜度纔是O(1),所以書中的這句話應該有錯。
另外需要說明的是書的所附光盤並沒有源代碼,只有幾個testcase,十分的遺憾(不知爲什麼不放,可能是怕盜版,大家拿到源碼後都不買書了吧:),不過這一點比之國外教材就差了)。而書中的源代碼中ReadStreamJava API中並不存在,kyeboard.readInt()也不知是從哪讀的數據(光盤上也沒有),反正代碼中是用這個類來初始化6 × 6, 6 × 8, 8 × 8, 8 × 10, 10 × 10, 10 × 12這些特殊規模棋盤的解的。這種缺憾使得源碼不能上機跑起來,我也沒有手工的跑這個程序去分析(有時間再說吧),另外書中的代碼的註釋實在是太少了。這些遺憾都使得該書對讀者來說不太方便。
【參考文獻】
[1] 《計算機算法設計與分析(第2版)》 王曉東 電子工業出版社
[2] 《算法設計與分析習題解答》 王曉東 清華大學出版社

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章