二路归并排序简介及其并行化
2015-05-08 17:46
190 查看
一、归并排序简介
1.算法思想
归并排序属于比较类非线性时间排序,号称比较类排序中性能最佳者,在数据中应用中较广。归并排序是分治法(Divide and Conquer)的一个典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
2.二路归并排序过程描述
设有数列{16,23,100,3,38,128,23}初始状态:16,23,100,3,38,128,23
第一次归并后:{6,23},{3,100},{38,128},{23};
第二次归并后:{3,6,23,100},{23,38,128};
第三次归并后:{3,6,23,23,38,100,128}。
完成排序。
3.二路归并复杂度分析
时间复杂度:O(nlogn),是最好、最坏和平均的时间性能,排序性能不受待排序的数据的混乱程度影响,比较稳定,这也是相对于快排的优势所在。空间复杂度为:O(n)。
稳定性:稳定,从上文排序过程中可以看出,黑体23一直在前面。
二、二路归并实现
1.C/C++串行实现
/************************************************ *函数名称:mergearray *参数:a:待归并数组;first:开始下标;mid:中间下标; * last:结束下标;temp:临时数组 *说明:将有二个有序数列a[first...mid]和a[mid...last]合并 *************************************************/ void mergearray(int a[], int first, int mid, int last, int temp[]) { int i = first, j = mid + 1,k =0; while (i <= mid && j <= last) { if (a[i] <= a[j]) temp[k++] = a[i++]; else temp[k++] = a[j++]; } while (i<= mid) temp[k++] = a[i++]; while (j <= last) temp[k++] = a[j++]; for (i=0; i < k; i++) a[first+i] = temp[i]; } /************************************************ *函数名称:mergesort *参数:a:待归并数组;first:开始下标; * last:结束下标;temp:临时数组 *说明:实现给定数组区间的二路归并排序 *************************************************/ void mergesort(int a[], int first, int last, int temp[]) { if (first < last) { int mid = (first + last) / 2; mergesort(a, first, mid, temp); //左边有序 mergesort(a, mid + 1, last, temp); //右边有序 mergearray(a, first, mid, last, temp); //再将二个有序数列合并 } }
本机测试100 * 1024 * 1024 =100M个32bits整型,串行需要15.536s,以下是本机软硬件参数,为Linux平台。
2.C/C++并行实现
2.1并行思路
将待排序数组通过偏移量进行逻辑切分为多块,将每个块传递给多个线程调用二路归并排序函数进行排序。待各个块内有序后,再合并各个块整合成有序数列。2.2并行代码
线程函数,供创建出来的线程调用。/******************************************* *函数名称:merge_exec *参数: para指针,用于接收线程下边,表示第几个线程 *说明: 调用二路归并排序 *******************************************/ void* merge_exec(void *para) { int threadIndex=*(int*)para; int blockLen=DataNum/threadNum; int* temp=new int[blockLen]; int offset=threadIndex*blockLen; mergesort(randInt,offset,offset+blockLen-1,temp); }
合并多个已经排好序的块。代码如下:
/*********************************************** *函数名称:mergeBlocks *参数: pDataArray:块内有序的数组 arrayLen:数组长度 * blockNum:块数 resultArray:存放排序的结果 *说明: 合并有序的块 ************************************************/ inline void mergeBlocks(int* const pDataArray,int arrayLen,const int blockNum,int* const resultArray) { int blockLen=arrayLen/blockNum; int blockIndex[blockNum];//各个块中元素在数组中的下标,VC可能不支持变量作为数组的长度,解决办法可使用宏定义 for(int i=0;i<blockNum;++i)//初始化块内元素起始下标 { blockIndex[i]=i*blockLen; } int smallest=0; for(int i=0;i<arrayLen;++i)//扫描所有块内的所有元素 { for(int j=0;j<blockNum;++j)//以第一个未扫描完的块内元素作为最小数 { if(blockIndex[j]<(j*blockLen+blockLen)) { smallest=pDataArray[blockIndex[j]]; break; } } for(int j=0;j<blockNum;++j)//扫描各个块,寻找最小数 { if((blockIndex[j]<(j*blockLen+blockLen))&&(pDataArray[blockIndex[j]]<smallest)) { smallest=pDataArray[blockIndex[j]]; } } for(int j=0;j<blockNum;++j)//确定哪个块内元素下标进行自增 { if((blockIndex[j]<(j*blockLen+blockLen))&&(pDataArray[blockIndex[j]]==smallest)) { ++blockIndex[j]; break; } } resultArray[i]=smallest;//本次循环最小数放入结果数组 } }
main函数中创建多线程完成并行排序,代码如下:
int main(int argc,char* argv[]) { int threadBum=8; int blockNum=threadNum; struct timeval ts,te; srand(time(NULL)); for(int i=0;i<DataNum;++i) { randInt[i]=rand(); } pthread_t tid[blockNum],ret[blockNum],threadIndex[blockNum]; //--------Two-way Merge Sort------- gettimeofday(&ts,NULL); for(int i = 0; i < threadNum; ++i) { threadIndex[i]=i; ret[i] = pthread_create(&tid[i], NULL,merge_exec,(void *)(threadIndex+i)); if(ret[i] != 0){ cout<<"thread "<<i<<" create error!"<<endl; break; } } for(int i = 0; i <threadNum; ++i) { pthread_join(tid[i], NULL); } mergeBlocks(randInt,DataNum,threadNum,resultInt); gettimeofday(&te,NULL); cout<<"MergeSort time: "<<(te.tv_sec-ts.tv_sec)*1000+(te.tv_usec-ts.tv_usec)/1000<<"ms"<<endl; }
8线程情况下,测试性能为4.223s,加速比3.68。针对机器的缓存大小,通过提高缓存命中率,可继续进行算法优化,提高排序性能。
参考文献
[1]http://baike.baidu.com/link?url=nQp1WY3UpyMMgJ1mr0qL6amEmDZZb2MLtxrwMTVIfFyaQaTAA1LXC5JqnrDqm_teLpX2TwCpKMdoPLXQ0jHrCa#6[2]http://blog.csdn.net/morewindows/article/details/6678165
相关文章推荐
- 二路归并排序简介及其并行化
- 二路归并排序及其改进方法
- 基数排序简介及其并行化
- 基数排序简介及其并行化
- 二路归并排序
- java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序
- 归并排序及其适用范围
- 二路归并排序
- 二路归并排序
- 二路归并排序
- 算法系列(四)归并排序及其改进(java实现)
- 二路归并排序
- 归并排序——二路归并排序
- CUDA(六). 从并行排序方法理解并行化思维——冒泡、归并、双调排序的GPU实现
- 归并排序及其时间复杂度分析
- CUDA(六). 从并行排序方法理解并行化思维——冒泡、归并、双调排序的GPU实现
- 二路归并排序
- Linux C学习笔记-排序算法6-二路归并排序
- 数据结构经典排序---二路归并排序
- CUDA系列学习(六) 从并行排序方法理解并行化思维——冒泡、归并、双调排序的GPU实现