前言
今天一次偶然的机会学习到了双调排序,还是感觉很有趣的,这种排序算法可以达到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)