zoukankan      html  css  js  c++  java
  • 二路归并排序简介及其并行化 分类: 算法与数据结构 2015-05-08 17:46 112人阅读 评论(0) 收藏

    一、归并排序简介

    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

    版权声明:本文为博主原创文章,未经博主允许不得转载。

    ---转载或者使用代码,请注明原作者 This is bill
  • 相关阅读:
    POCO库——Foundation组件之日期时间DateTime
    POCO库——Foundation组件之加解密Crypt
    POCO库——Foundation组件之缓存Cache
    POCO库——Foundation组件之核心Core
    POCO库——Foundation组件概述
    HP-SOCKET TCP/UDP通信框架库解析
    Notepad++ 使用nppexec插件配置简易开发环境
    Duilib源码分析(五)UI布局—Layout与各子控件
    Breakpad Google的crash捕获、抓取开源库
    Pugixml一种快速解析XML文件的开源解析库
  • 原文地址:https://www.cnblogs.com/ScytheWH/p/4622975.html
Copyright © 2011-2022 走看看