並行算法——雙調排序

  • O(nlog^{2}n)Bitonic Sequences

首先來介紹一下什麼叫做雙調序列,我認爲,這個序列是一個先增再減的序列(或者先減再增),判斷方法是這樣滴,首先,將序列第一個元素和最後一個元素連一條線,構成一個環,然後計算每條線的增減性,如果是下面這樣的序列,就可以叫做Bitonic Sequences,在算法中,通常要求長度是2**k

再舉個簡單的例子:

好了,這就是雙調排序的基礎,下面我們來看一下如何利用這種雙調序列

  • Bitonic Split

這一步做了什麼呢,其實很簡單,就是將一個雙調序列分成兩個雙調序列

怎麼分呢,

A[0]=min(A[0],A[\frac{n}{2}])

A[\frac{n}{2}] = max(A[\frac{n}{2}], A[0])

這樣,就形成了一個[0, \frac{n}{2})的雙調序列和一個[\frac{n}{2}, n)的雙調序列,如下圖所示

且你會發現左邊的最大值會小於等於右邊的最小值,這就爲我們實現並行提供了基礎:左右同時計算即可

  • Bitonic Merge

這個就是那個排序的函數了,僞代碼也比較好寫

void BitnoicMerge(A[0..n-1])
{
    //要求n是偶數
    if(n>=2)
    {
         BitnoicSplit(A[0..n-1]);
         BitnoicMerge(A[0..n/2-1]); //這兩個BitnoicMerge是可以並行執行的
         BitnoicMerge(A[n/2..n-1]);
    }

}

可是,一般序列都是任意的,我們怎麼樣把它轉化成一個雙調序列呢?

  • How to form a Bitonic Sequence

這裏我們也是用了一下遞歸分治的思想,你想啊,如果一個序列前半段是增序列,後半段是減序列,那麼這個序列不就是一個雙調序列了麼,且如果一個序列的size是2,他也是一個雙調序列

僞代碼如下:

void genBitonic(A[0..n-1])
{
    genBitonic(A[0..n/2-1]);
    genBitonic(A[n/2..n-1]);

    BitonicMerge_add(A[0..n/2-1]);
    BitonicMerge_minus(A[n/2..n-1]); //前半段增序後半段降序,這不就是一個雙調序列麼,可是這個函數只能處理雙調序列的排序,所以前面要有genBitonic
}
  • 複雜度分析

該算法串行時間複雜度O(nlog^{2}n),span complexity是O(log^{2}n), 分析過程如下

首先,我們可以看到,該算法主要的複雜度在genBitonic上面,那麼這個過程是什麼樣的呢,可以通過下面這張圖片來理解

可以看到,其DAG圖計算的span complexity爲1+2+...+log n,即O(log^{2}n),這個span沒有計算並行for循環的span,將其視作O(1)而不是O(log n),至於work complexity呢,可以觀察上圖每一個span節點,其同階段節點共有n/2個(因爲兩兩比較),然後就可以輕鬆計算出串行時間複雜度O(nlog^{2}n)了,這個看圖比較容易理解,看代碼反而不容易理解了

至於BitonicMerge的時間複雜度啥的,可以通過上圖的一個同色方框來理解,其span complexity是O(log n), work complexity是O(nlog n)

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