双调排序

前言

今天一次偶然的机会学习到了双调排序,还是感觉很有趣的,这种排序算法可以达到O(n(logn)(logn))的时间复杂度,更重要的是这种算法很好地支持了并发计算。
这里写图片描述
这里写图片描述
循环对S1,S2再使用相同的方法,直到S1与S2只有1元素,再重新整合起来即可。但是我们需要先使用某种方法先让数列成为一个双调序列。
具体的方法是:从小到大聚合
显然当数列只有一个元素时,就已经是一个特殊的双调序列了,我们此时把这些只含单个元素的数列相邻不重叠的两两比较形成:递增、递减、递增、递减•••交替出现的数列。
然后我们再把这个数列相邻不重叠4个为一组。组内依次为递增、递减、递增、递减•••
然后再把相邻两个双调子数列合并成同一个,最后只就剩下左边递增、右边递减的双调数列。再把这两个大的子序列整合起来就是一个有序数列啦.

举例:

A={5,2,1,7,3,8,6,4}:
第一步,将A里面的相邻两个元素两两比较,两个数为一组,各组内有序,各组为升序、降序交替。
A={2,5,7,1,3,8,6,4}
第二步:在第一步的基础上,两个组各并为一个大组,大组内有序,大组与大组之间升序、降序交替出现。
前两个小组组成一个大组:A’={2,5,7,1}
后两个小组组成一个大组:B’={3,8,6,4}
我们让A’升序,B’降序。
首先:
A’={2,5,7,1}
A’已经是一个双调序列了,先增后减。
那么我们把A’的前一半记做:A’L={2,5},后一半记做:A’R={7,1}
则对于A’来说:
S1={min(2,7),min(5,1)}={2,1}
我们再将S1分成两部分:S1’L={1},S1’R={2}
那么对于S1={2,1}:
它的S1={min(2,1)}={1},S2={max(2,1)}={2};
所以S1={2,1}最后成为{1,2}内部升序。
S2={max(2,7),max(5,1)}={7,5}
那么对于S2={7,5}:
它的S1={min(7,5)}={5},S2={max(7,5)}={7}
所以S2={7,5}最终变换为{5,7}
此时A’L=S1,S2={1,2},{,5,7}=>{1,2,5,7} 前面一个组已经升序。

再看后面一个组B:需要成为降序
B’={3,8,6,4},
把B’分成两边:B’L={3,8},B’R={6,4}
则S1={max(3,6),max(8,4)}={6,8}
S2={min(3,6),min(8,4)}={3,4}
对于S1再进行双调排序,同时排序为降序:S1={8,6}
同理:S2={4,3}
所以B’=S1,S2={8,6,4,3}
所以A=A’,B’={1,2,5,7,8,6,4,3}
此时A已经是一个双调序列了,两个大组,第一大组A’先增,第二个大组B’后减。
再将两个大组合成一个更大的组A’,B’让它递增。可以发现A’B’就是构成了A本身。
A={1,2,5,7,8,6,4,3}
左边:L={1,2,5,7}
右边:R={8,6,4,3}
S1={1,2,4,3} =>{1,2,4,3}=>{1,2,3,4}
S2={8,6,5,7}=>{5,6,8,7}=>{5,6,7,8}
最后:A=S1,S2={1,2,3,4,5,6,7,8}

为什么说它很好支持并发呢?
由上面,我们可以知道,当一个序列是双调时,生成出S1,S2后。S1与S2已经是分离开来了,S2的任何元素都大于S1的任何元素。所以在S1,S2内部的排序中,可以使用两个线程分别处理其中的一个,并发的执行。

处理数列长度不是2的幂情况

上面的性质利用的是 数列长度为2的幂的特殊双调序列的性质,当长度不为2的幂可以使用很大的标志数填充原序列,直到序列的长度为2的幂,只取前原长度个数即是原序列的排序。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <iostream>
using namespace std;

#define DBMAX   ((unsigned short)((1 << (15 - _DOFF)) - 1))

//定义Double 可以表示的最大值.用于填充。注意并不float max.因为 传入的参数可能会取值到float max。

void print(float *d, int l, int r, int epoch = 0)
{
    printf("epoch %d:\n", epoch);
    for (int i = l; i < r; i++)
    {
        printf("%f  ", d[i]);
    }
    printf("\n");
}

void biSort(float * d, int l, int r, int order = 0)
//order=0升序,order=1降序
//l是左起点,r是右结束点的后一个[l,r),左开右闭
//该函数将d[l]~d[r-1]的元素按序order进行双调排序
{
    int rr = r;
    int ll = l;
    int n = r - l;//真实的长度
    int step = 0;
    int sec = 1;
    while (sec < n)
    {
        sec = sec * 2;//分区数,对于组内,先分成sec组
        step = n / sec;//每组含有step个元素
                        //step会越来越小,其实就是越来越是相邻的数据进行比较
        for (int ii = 0; ii < sec; ii+=2)
            //每个区进行处理
        {
            l = ll + ii*step;
            r = l + step;
            if (order == 0)
                //升序处理
            {
                for (int i = 0; i < step; i++)
                {
                    if (d[i + l] != d[i + l] || d[i + l + step] != d[i + l + step])
                        //sqrt(-1),处理纯虚数
                    {
                        continue;
                    }
                    float min = d[i + l]<d[i + step + l] ? d[i + l] : d[i + step + l];
                    float max = d[i + l]>d[i + step + l] ? d[i + l] : d[i + step + l] ;
                    d[i + l] = min;//小的放s1
                    d[i + l + step] = max;//大的放s2
                }
            }
            else
                //降序处理
            {
                for (int i = 0; i < step; i++)
                    //递减序列
                {
                    if (d[i + l] != d[i + l] || d[i + l + step] != d[i + l + step])
                        //sqrt(-1),处理是否是虚数
                    {
                        continue;
                    }
                    float min = d[i + l]<d[i + step + l] ? d[i + l] : d[i + step + l];
                    float max = d[i + l]>d[i + step + l] ? d[i + l] : d[i + step + l];
                    d[i + l] = max;
                    d[i + l + step] = min;
                }
            }

        }
    }

}
void semiSort(float * data, int l, int r)
{
    int num = r - l;
    int expnum = pow(2.0,int( log2(num*1.0)+0.5));
    float *dd=data;
    if (num!=expnum)
    {
        dd = (float*)malloc(expnum*sizeof(float));
        for (int i = 0; i<expnum; i++)
        {
            if (i<num)
            {
                dd[i] = data[i+l];
            }
            else
            {
                dd[i] = DBMAX;
            }
        }

    }
    //以上在进行必要的填充
    int batch = expnum;//expect number ,把目前的元素分成几组进行双调排序
    char flag = 0;//用于标记降序和升序
    int Cnt = 0;
    while (batch>1)
    {
        batch = batch / 2;//每一次迭代,组数都会减半。
        int num_of_each_batch = expnum / batch;//每一组的元素个数
        for (int j = 0; j < batch; j++)
        {
            int start; 
            int end;
            if (dd == data)
                //没有动态分配的则需要偏移
            {
                start = l + j*num_of_each_batch;
            }
            else
            {
                start = j*num_of_each_batch;    
            }
            end = start + num_of_each_batch;
            biSort(dd, start, end, flag);//调用biSort进行双调排序

            print(dd, 0, expnum, Cnt += 1);
            flag = !flag;//升序和降序的符号相反
        }
    }
    if (dd!=data)
    {
        for (int i = l; i < r;i++)
        {
            data[i] = dd[i-l];
        }
        free(dd);
    }
}
void segmentedBitonicSort(float* data, int* seg_id, int* seg_start, int n, int m)
//分段
{

    for (int i = 0; i < m; i++)
    {
        int l = seg_start[i];
        int r = seg_start[i + 1];
        semiSort(data, l, r);
    }
}

int main()
{
    float data[] = { 5, 2, 1, 7, 3, 8, 6, sqrt(-1) };
    int seg_id[] = { 0, 0, 0,0,0,0,0,0};
    int seg_start[] = { 0, 8 };
    int n = 8;
    int m = 1;
    int l = 0;
    int r = sizeof(data) / sizeof(float);
    segmentedBitonicSort(data, seg_id, seg_start, n, m);
    //print(data, l, r);
    //float d[] = { 0.4, 0.6, 0.5 };
    //semiSort(d, 0, 3);
    //print(d,0,3);
    system("pause");
    return 0;
}

并没使用递归,可以处理输入为sqrt(-1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值