最近由于需要在GPU上做排序,因为GPU的并行性,所以需要一个并行的排序算法。而BitonicSort双调排序是一个非常适合并行化的排序算法,其在《算法导论》的排序网络一章对其基本原理做了描述与证明。有兴趣大家可以看看。
作为个人的一个学习总结,这里只是对其性质做些简单介绍,以及它算法基本思想和几个重要的步骤。如果需要了解BitonicSort更加详细的理论证明,可以参考《算法导论》。
首先BitonicSort的算法复杂度为O(n(logn)^2),看到这里大家会问比快排O(nlogn)要慢啊,干嘛要用它?但是它的n在并行机(比如GPU)上可以并行化掉n,那么复杂度就变为O( (logn)^2 )了,自然比快排等要快。
下面说下它算法思想和为什么它适合并行化。
BitonicSort主要算法思想:以有n个元素的0-1数组array递增排序为例。
第一步:先数组划分成两半,一半是Bitonic递增序列,一半是Bitonic递减序列。Bitonic递增序列可以理解为可以先递增,后递减(也可以只递增),比如:000111000,0001111;反之Bitonic递减序列形如1111000011111,11111111111110000000000000。
第二步:将元素array[i](0<=i<n/2)与array[i+n/2]比较
If(array[i]>array[i+n/2])
Exchange(array[i],array[i+n/2]);
然后大家可以简单地证明:数组最小的n/2个元素在前面一半,最大n/2个元素在后面一半。继而不断递归便可得到有序数组。从上面可以看到BitonicSort也存在一个致命的缺点,因为不断的二分,所以原始的BitonicSort只能够排序2^n长度的数组,如何改进下一次再补充。
为了突出关键,这里只贴出关键部分的代码,其中主要调用SortUp(int beg,int n)这个函数,按照递增排序数组从beg开始的n个元素
int main()
{
BitonicSort biSort;
int n=1<<15;
biSort.InitRandData(n);
biSort.SortUp(0,n);
cout<<"The Sorted Result is:"<<endl;
biSort.OutputArray();
}
void BitonicSort::SortUp(int beg,int n)
{
if(n==1)return;
SortUp(beg,n/2);//把前面的n/2个元素按递增排序
SortDown(beg+n/2,n/2);//把后面n/2个元素按递减排序
MergeUp(beg,n);//合并前面n/2个和后面n/2个数组。
return;
}
void BitonicSort::SortDown(int beg,int n)//类似SortUp,只是按照递减排序数组从beg开始的n个元素
{
if(n==1)return;
SortUp(beg,n/2);
SortDown(beg+n/2,n/2);
MergeDown(beg,n);
}
void BitonicSort::MergeUp(int beg,int n)
{
if(n==1)return;
int halfN=n>>1;
for(int i=beg;i<beg+halfN;i++)
{
if(array[i]>array[i+halfN])Exchange(array[i],array[i+halfN]);
}
MergeUp(beg,halfN);
MergeUp(beg+halfN,halfN);
return;
}
void BitonicSort::MergeDown(int beg,int n)
{
if(n==1)return;
int halfN=n>>1;
for(int i=beg;i<beg+halfN;i++)
{
if(array[i]<array[i+halfN])Exchange(array[i],array[i+halfN]);
}
MergeDown(beg,halfN);
MergeDown(beg+halfN,halfN);
return;
}