Bitonic Sequences
首先來介紹一下什麼叫做雙調序列,我認爲,這個序列是一個先增再減的序列(或者先減再增),判斷方法是這樣滴,首先,將序列第一個元素和最後一個元素連一條線,構成一個環,然後計算每條線的增減性,如果是下面這樣的序列,就可以叫做Bitonic Sequences,在算法中,通常要求長度是2**k
再舉個簡單的例子:
好了,這就是雙調排序的基礎,下面我們來看一下如何利用這種雙調序列
- Bitonic Split
這一步做了什麼呢,其實很簡單,就是將一個雙調序列分成兩個雙調序列
怎麼分呢,
這樣,就形成了一個的雙調序列和一個
的雙調序列,如下圖所示
且你會發現左邊的最大值會小於等於右邊的最小值,這就爲我們實現並行提供了基礎:左右同時計算即可
- 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
}
- 複雜度分析
該算法串行時間複雜度,span complexity是
, 分析過程如下
首先,我們可以看到,該算法主要的複雜度在genBitonic上面,那麼這個過程是什麼樣的呢,可以通過下面這張圖片來理解
可以看到,其DAG圖計算的span complexity爲1+2+...+log n,即,這個span沒有計算並行for循環的span,將其視作O(1)而不是O(log n),至於work complexity呢,可以觀察上圖每一個span節點,其同階段節點共有n/2個(因爲兩兩比較),然後就可以輕鬆計算出串行時間複雜度
了,這個看圖比較容易理解,看代碼反而不容易理解了
至於BitonicMerge的時間複雜度啥的,可以通過上圖的一個同色方框來理解,其span complexity是O(log n), work complexity是O(nlog n)