合併插入排序
Lester Ford, Jr. 以及 Selmer Johnson 把 Howard B. Demuth 的 1957 年博士論文裡面提到的 5 筆資料排序方法進行推廣,最終獲得一個用謹慎的方法試圖減少比較次數的排序方法──合併插入排序 Merge Insertion Sort。 這個排序法的名字是高德納教授(Donald Knuth)在他撰寫的《The Art of Computer Programming》裡面取的。 附帶一提,最近 MIT 教授 Lex Fridman 釋出了一段與 Knuth 的訪談1:https://www.youtube.com/watch?v=2BdBfsXbST8 大家有興趣可以聽聽看。
如何避免大量的天花板
演算法的核心概念是這樣的:如果看到一個長度為 2k−1 元素的序列,那麼此時將一個新的元素插入其中,是最不會浪費「資訊」的。因為資訊理論下界 \ceillog2((2k−1)+1) 無論有沒有天花板,其數值都是一樣的,得花費恰巧 k 次才能夠找出新元素的落點。
如果長度不到 2k−1 的序列怎麼辦?盡量讓這件事情不要發生就好了! 乍看之下很困難啊──但是 Ford-Johnson 盡量做到了,最重要的想法可以透過下面這張圖來傳達:
假設我們能夠經過若干次比較之後,把所有元素的大小關係描述成上面這張圖。其中 x→y 代表經過比較以後,我們得出了 x<y 這項結論。 上圖黃色框線內共有 7 個元素,若我們想將紅色元素插入上面長鏈中,此時恰好達到資訊理論下界的 log2(7+1)=3 次。因此可以得到結論是:此時優先將紅色元素插入長鏈、再將左邊的藍色元素插入長鏈。其需要的比較次數至多是 log2(7+1)+log2(7+1)=6 次。相反地,若我們先插入藍色元素,再插入紅色元素,在最壞情形下我們得花費 log2(7+1)+\ceillog2(8+1)=7 次比較才能達到效果。
行文至此,不難發現,如果我們有辦法把輸入資料的大小關係,表達成上圖這種牙刷形狀,再依照最不浪費比較次數的方式進行二分插入,說不定可以得到較佳(比較次數較小)的排序演算法。
合併插入排序的第一步
要怎麼生出牙刷呢? 首先,在分而治之的部分,我們先將資料隨意地兩兩分成一組,並且花費 \floorn/2 次比較。 如果有多出來的元素,就先放在旁邊吧。 接下來,我們可以遞迴針對比較大的那些 \floorn/2 元素進行排序,就可以把它們接成一長串了! 最後是刷毛整理的部分:我們將刷毛由左至右(資料可以命名為 b1,b2,…,b\ceiln/2)分成若干組,而每一組的數量都會滿足:把這組資料由右至左依序進行二分插入法,都是最不浪費資訊的。
我們現在來引用 Donald Knuth 《The Art of Computer Programming》裡面提到的分析技巧2:
這些組別分起來,會長得像這樣:{b1}、{b2,b3}、{b4,b5}、{b6,b7,b8,b9,b10,b11}…。我們可以令 tk 代表最大的註標使得把跟它分派到同一組的所有元素,依照反過來的順序依序二分插入序列時,最壞情形下每次都恰好需要 k 次比較。即,跟 t0 同一組的資料插入序列恰好需要 0 次比較、跟 t2 同一組的資料插入序列需要 2 次比較、依此類推。
要怎麼找出 tk 的值呢?注意到,當 btk 的筆資料被插入序列的時候,所有註標不超過 tk−1 的資料已經被加入序列了。此外,所有註標等於 tk−1+1,…,tk 的元素都還沒有被加入序列。也就是說,此時序列恰恰好有 2tk−1+(tk−tk−1−1)=tk−1+tk−1 筆資料!(這個 −1 是因為最後一個註標 btk 頭頂上那筆資料不需要被考慮進去,它的資料比所有人都來得大)為了讓資訊不浪費,我們希望 tk−1+tk−1=2k−1。因此得到 tk−1+tk=2k。 經過一番寒徹骨,不是,經過一翻兩瞪眼以後,不是,總之,經過一直翻一直翻,我們可以得出一個神奇的結論: tk=2k−2k−1+2k−2−⋯+(−1)k20=(2k+1+(−1)k)/3.
有了這個演算法以後,我們就可以利用遞迴方法來分析,這個合併插入排序需要的比較總次數了。我們令 F(n) 表示對 n 筆資料進行 Merge-Insertion Sort 最壞情形下所需要的比較次數。 那麼可以得到遞迴關係:
F(n)=\floorn/2+F(\floorn/2)+G(\ceiln/2)
其中,G(\ceiln/2) 表示著當牙刷刷毛有 \ceiln/2 根的時候(分別是 b1,b2,…,b\ceiln/2),要合併成一條單鏈需要的比較次數。藉由 {ti} 的表示法,若 tk−1<\ceiln/2≤tk,我們可以把 G(\ceiln/2) 寫成
G(\ceiln/2)=k−1∑i=1i(ti−ti−1)+k(\ceiln/2−tk−1)=k\ceiln/2−(t0+t1+⋯+tk−1)
令 wk=t0+t1+⋯+tk−1=\floor2k+1/3。 現在來證明今天最重要的一個結論:
引理 22
F(n)−F(n−1)=k 若且唯若 wk<n≤wk+1。
證明
我們可以用數學歸納法。Base Case 很顯然,所以就不寫了。Inductive Case 的部分可以利用 n 的奇偶性分別討論:如果 n 是偶數,那麼 F(n)−F(n−1)=1+F(n/2)−F(n/2−1),後半部的數值等於 k−1 若且唯若 wk−1<\floorn/2≤wk。而由 wk 之定義可知
wk−1<n/2≤wk(n 是偶數。)⟺\floor2k/3<n/2≤\floor2k+1/3⟺2\floor2k/3<n≤2\floor2k+1/3⟺2\floor2k/3+1<n≤2\floor2k+1/3(n 是偶數,這很重要。)⟹\floor2k+1/3<n≤\floor2k+2/3
於是 n 是偶數的時候結論成立。第二種情形,當 n 是奇數的時候,我們可以依樣畫葫蘆:
F(n)−F(n−1)=G(\ceiln/2)−G(\ceil(n−1)/2)
然後這個值是 k 若且唯若 tk−1<\ceiln/2≤tk,然後這個等價於 wk<n≤wk+1,得證。
有了引理 22 以後,我們試圖找出 k 與 n 之間的關係。因為 wk<n≤wk+1,所以 k 可以寫成 \ceillog234n。於是得到很酷的結論:
F(n)=n∑i=1\ceillog234i≈nlog2n−1.415n+O(log2n)
還記得二分插入法的上界、以及資訊理論下界嗎?我們把它們同步列出來:
A(n)=\ceillogn!≈nlog2n−1.443n+O(log2n)
B(n)=n∑i=1\ceillogi≈nlog2n−0.915n+O(log2n)
不難發現 F(n) 比起二分插入法得到的比較次數 B(n),更接近資訊理論下界 A(n) 了呢!
結論
後話就是,在 1979 年 Manacher3 用了混合方法,把 Hwang-Lin4 的兩序列合併演算法考慮進去並且在某些情形下改良,同時改進了 Ford-Johnson 演算法,並且證明了存在無窮多個 n,其最少比較排序的比較次數比 F(n) 嚴格來得小,該方法贏過 FJ 的最小值是 n=189。然後到了 1985 年 Bui 和 Thanh5 再次修改了 Manacher 演算法,得出在絕大多數的 n 時,FJ 演算法不是最優的。而且也得出了最小反例出現在 n=47(到 2007 年為止,Peczarski6 聲稱 FJ 演算法在 n<47 的時候是最佳解,Peczarski 證明了利用某一類型的分而治之演算法無法在 n≤46 的時候贏過 FJ 演算法。)
然後 F(n) 其實有封閉形式(Closed Form,俗稱公式解): F(n)=n\ceillog234n+\floor132\floorlog26n+\floor12log26n。
最後,大家要多多刷牙喔 ^_<。
合併插入排序法中間的分析,利用到了「如何從排序到一半的東西,利用盡量少比較次數完成排序」的特性。我們是否可以把這個概念推廣一下呢?排序到一半的東西,可以被表示成一個叫做 偏序集(Partial Ordered Set) 的東西。我們能否從偏序集獲得一些排序知識呢?
推薦閱讀
- 維基百科:https://en.wikipedia.org/wiki/Merge-insertion_sort
- Ford-Johnson 演算法當年的論文:https://www.jstor.org/stable/pdf/2308750.pdf
感謝 a127 的告知與推薦!
Donald Knuth, The Art of Computer Programming, Volumn 3, Page 183-187.
Glenn K. Manacher, The Ford-Johnson Sorting Algorithm Is Not Optimal, 1979. https://dl.acm.org/doi/pdf/10.1145/322139.322145
T. D. Bui and Mai Thanh, Significant improvements to the Ford-Johnson algorithm for sorting, BIT 1985. https://link.springer.com/article/10.1007/BF01934989
交大黃光明教授與 S. Lin(我查不到…)當年在貝爾實驗室發表的論文:F. K. Hwang and S. Lin, A Simple Algorithm for Merging Two Disjoint Linearly Ordered Sets, SIAM Journal of Computation, 1972. https://epubs.siam.org/doi/abs/10.1137/0201004
Marcin Peczarski, The Ford–Johnson algorithm still unbeaten for less than 47 elements, IPL 2007 February. https://www.sciencedirect.com/science/article/pii/S0020019006002742