OI之外的一些東西:簡單談談排序網絡

OI之外的一些東西:簡單談談排序網絡

    我們之前所有的排序算法都是給定了數據再進行排序,排序的效率很大程度上取決於數據的好壞。我們今天所介紹的是一個完全不同的排序方法,它可以在“暗箱”裏對數據進行排序(即你不必知道實際數據是什麼),換句話說這種排序方法不依賴於數據(Data-Independent),所有比較操作都與數據無關。你甚至可以立即忘掉前面的比較結果,因爲對於所有可能的數據這類排序算法都能得到正確答案並且排序步驟完全相同。本文結束後再回過頭來看這段話你將有更深的認識。
  
    我們設置一個暗箱,暗箱左邊有n個輸入口,暗箱右邊有n個輸出口。我們需要設計一個暗箱使得,任意n個數從左邊輸進去,右邊出來的都是有序的。圖1顯示了有4個輸入的暗箱。
  
    暗箱裏唯一允許的元件叫做“比較器”(Comparator),每個比較器連接兩個元素,當上面那個比下面那個大時它將交換兩個元素的位置。也就是說,每經過一個比較器後,它的兩端中較小的一個總是從上面出來,較大的總是到了下面。圖2顯示了一種包含4個比較器的暗箱系統。當輸入數據3,1,4,2通過這個系統時,輸出爲1,3,2,4,如圖3所示。這種暗箱結構叫做比較網絡(Comparator Network)。如果對於任意一個輸入數據,比較網絡的輸出都是有序的,那麼這個比較網絡就叫做排序網絡(Sorting Network)。顯然,我們例子中的比較網絡不是一個排序網絡,因爲它不能通過3,1,4,2的檢驗。

    現在,我們的第一個問題是,是否存在比較網絡。就是說,有沒有可能使得任意數據通過同一組比較器都能輸出有序的結果。我們最初的想法當然是,把我們已知的什麼排序算法改成這種形式。把原來那十種排序又翻出來看一遍,找一找哪些排序的比較操作是無條件的。運氣不錯,我們所學的第一個算法——冒泡排序,它的比較就是無條件的,不管數據怎樣冒泡排序都是不斷比較相鄰元素並把較小的放到前面。冒泡排序是一個徹頭徹尾的排序網絡模型,我們可以立即畫出冒泡排序所對應的排序網絡(圖4)。這是我們得到的第一個排序網絡。我們通常不認爲插入排序是排序網絡,因爲插入排序的比較次數取決於數據的有序程度。
  
    傳統的計算機一次只能處理一個比較。排序網絡真正的研究價值在於,假如有機器可以同時處理多個比較器,排序的速度將大幅度提高。我們把比較器的位置稍微移動一下,把那些互不衝突(處理的元素不同)的比較器壓縮到一層(Stage)(圖5),這樣整個排序過程壓縮爲了2n-3層。實現排序網絡的機器可以在單位時間裏並行處理同一層中所有的比較。此時,比較次數的多少對排序效率不起決定作用了,即使比較次數多一些但是排序網絡的層次更少,效率也會更高一些。我們自然又想,排序網絡需要的層數能否少於2n-3。我們想到,圖5的左下角和右下角似乎有些空,我們期望能在這些位置加一些比較從而減少層數。圖6給出了一個只有n層的排序網絡,這叫做奇偶移項排序(Odd-even Transposition Sort)。我們下文將證明它確實是一個排序網絡。這次的圖很多,排版也很困難,累死我了。我把下面的圖7也放到這裏來了,不然到處都是圖很難看。
  

    給出一個比較網絡,怎樣判斷它是不是一個排序網絡?很遺憾,現在還沒有找到一種好的算法。事實上,這個問題是一個NPC問題。注:這種說法是不準確的,因爲目前還沒有跡象表明這個問題是NP問題。準確的說法應該是,“判斷某比較網絡爲排序網絡”是Co-NP Complete,而“判斷某比較網絡不是排序網絡”(即找到一個反例)纔是NP Complete。
    傳統的做法是枚舉所有n的排列來驗證,一共要考慮n!種情況。下面我們介紹排序網絡理論裏最重要的結論:0-1原理(0-1 Principle)。使用這個原理來驗證排序網絡只需要考慮2^n種情況。0-1原理告訴我們,如果所有的01序列能夠通過比較網絡排出順序,那麼這足以說明該網絡爲排序網絡。證明過程很簡單。爲了證明這個結論,我們證明它的逆否命題(逆否命題與原命題同真假):如果一個比較網絡不是排序網絡,那麼至少存在一個01序列不能被排序。我們給出一種算法,這個算法可以把任何一個不能被排序的輸入數據轉化爲一個不能被排序的01序列。
    在最初的例子(圖3)中,輸入數據3,1,4,2的輸出爲1,3,2,4,沒有成功地排出順序,從而判斷出該網絡不是排序網絡。這說明,輸出結果中存在逆序對(左邊某個數大於右邊的某個數)。我們從輸出結果中找出一個逆序對來。例子中,(3,2)就是我們要找的數。現在,我們把輸入中所有小於數字3(左邊那個數)的數都變成0,把所有大於等於3的數都變成1。這樣,3,1,4,2就變成了1,0,1,0。顯然,把得到的這個01序列輸入進去,原來曾經發生過交換的地方現在仍然會交換,原來不曾交換的地方現在也同樣不會發生交換(當兩個0或兩個1進行比較時,我們既可以認爲它們不交換,也可以看成它們要互相交換,反正都一樣)。最後,該01序列輸出的結果中,本來3的位置現在還是1,原來2的位置現在仍然是0,逆序對仍然存在。因此,只要一個比較網絡不是排序網絡,那麼總可以找到一個01序列不能被排序。等價地,如果所有的01序列都能被排序了,這個比較網絡也就是排序網絡了。

    我們用0-1原理來證明奇偶移項排序的正確性。我們對n進行數學歸納證明。n=2時(一個“工”字)顯然是排序網絡。
    圖中是n=8的情況。我們假設對於所有n<=7,奇偶移項排序網絡都是正確的。我們同時假定所有輸入數字非0即1,下面我們說明n=8時所有的01序列都能被正確排序。
    假設最後一個數是1(圖7,在前面的),那麼這個1將始終排在最後不參與任何交換操作,對前面7個數沒有任何影響。除去無用的灰色部分,剩下的就是n=7這一規模較小的子排序網絡,由歸納假設則n=8也是排序網絡;
  
    假設最後一個數是0(圖8),那麼在每一次比較中這個0都會被提到前面去(前面說過,兩個0之間交不交換是一回事)。藍色的箭頭表示每個數跑到了什麼位置。你會發現除最後一個數以外前7個數之間的比較器又構成了n=7的情況。

    接下來,我們提出一些比較器個數爲O(n*logn*logn)的排序網絡。其中一種就是之前提到過的2^p*3^q增量Shell排序。這種增量排序的特點是每一趟排序中的每個數只與前面的數比較一次,因此它可以非常方便地轉化爲排序網絡。圖9就是一個n=8的Shell排序網絡。Bitonic排序也可以做到O(n*logn*logn)的比較器個數,今天不介紹它。下面詳細介紹奇偶歸併排序網絡。
  
    奇偶歸併排序網絡也是一種比較器個數爲O(n*logn*logn)的排序網絡。它和歸併排序幾乎相同,不同的只是合併的過程。普通歸併排序的O(n)合併過程顯然是依賴於數據的,奇偶歸併排序可以把這個合併過程改成非數據依賴型,但複雜度將變高。這個合併過程本身也是遞歸的。我們假設n是2的冪(不是的話可以在前面添0補足,這對複雜度的計算沒有影響),算法首先把n個數中所有的奇數項和偶數項分別遞歸地合併,然後在排序後的第i個偶數項和第i+1個奇數項之間設立比較器。
    假如1,4,6,8和2,3,7,9是兩段已經有序的子序列,合併過程首先遞歸地合併1,6,2,7和4,8,3,9,這樣原數列就變成了1,3,2,4,6,8,7,9。然後分別把(3,2),(4,6),(8,7)三對相鄰元素中各自較小的那個交換到前面,完成合並操作。使用0-1原理證明這個結論出乎意料的簡單:圖10顯示了n=16的情況,白色的方格代表一個0,灰色方格代表1。奇偶項分別排序後,偶數項1的個數最多比奇數項多出2個,我們設立的比較器可以考慮到所有的情況,不管什麼情況都能讓它最終變得有序。
  
    由前面說過的結論,合併過程總共需要比較O(nlogn)次。歸併排序遞歸一共有O(logn)層,每一層總的比較器個數不超過O(nlogn),因此總共O(n*logn*logn)。一個n=8的完整的奇偶歸併排序網絡如圖11所示。

    菜鳥獻醜,漏洞百出。如果我有什麼錯誤,各位大牛請指正。
    Matrix67原創,轉載請註明出處。

  外部排序(External Sort)已經在這裏提到過,不再說了。
  所有排序的知識到這裏說完了,下次再發布的就是數論相關內容了。數論部分將從進位制開始談起。
  我會一直寫下去,本人活到什麼時候寫到什麼時候寫完爲止。不過,這幾天緩一下,我計劃做一個PJBlog的單版面論壇模塊。 
 
發佈了2 篇原創文章 · 獲贊 13 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章