您的位置:首页 > 编程语言 > C语言/C++

C++实现分段双调排序算法

2015-10-14 22:12 459 查看
C++实现分段双调排序算法

//问题说明:
//
//给出分成m段 的n个浮点数,输入数据已按段号有序,但每段内部 无序。用C/C++
//编写一个分段双调排序(Bitonic sort)函数,对每一段内部的浮点数进行排序,但
//不要改变段间的位置。
//接口方式:
//void segmentedBitonicSort(float* data, int* seg_id, int* seg_start, int
//n, int m);
//
//输入数据中,data包含需要分段排序的n个float值,seg_id给出data中n个元素各
//自所在的 段编号。seg_start共有m+1个元素,前m个分别给 出0..m-1共m个段的起
//始位置,seg_start[m]保证等于n。
//
//seg_id中的元素保证单调不下降,即对任意的i<j,seg_id[i]<=seg_id[j]。
//seg_id所有元 素均在0到m-1范围内。
//
//输出结果覆盖data,保证每一段内排序,但不改变段间元素的顺序

#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

void segmentedBitonicSort(float* data, int* seg_id, int* seg_start, int n, int m)
{
////判断输入是否有误
if(n <= 0 || m <= 0||m>n)
{
cout<<"Input error!n>m>0"<<endl;
return;
}
if(!(seg_start[m]==n && seg_id[n-1]==(m-1)))
{
cout<<"Input error! seg_start[m]==n,seg_id[n-1]==(m-1)"<<endl;
return;
}
//先段内排序
for(int d=0;d<m;d++)
{
int len=1;
while (len < seg_start[d+1]-seg_start[d])//循环退出的条件
len = len << 1;//寻找大于n的最小的2的幂次方len

float Max=999999;//作为填充数
vector<float> segdata(len);
// vector<float> segdata(len);
for (int i = 0; i < seg_start[d+1]-seg_start[d]; i++)
{
segdata[i]=data[seg_start[d]+i];
}
//如果len > n,就说明数组的个数不够,要将个数填充到len个
for (int i = seg_start[d+1]-seg_start[d]; i < len; i++)
segdata[i] =Max;
///////对整个数组进行排序
for (int step = 2; step <= len; step <<= 1)
{
//////内部循环可任意交换
for (int i = 0; i < len; i += step<<1)//1
{
///升序排
for (int step0 = step>>1 ;step0 >0 ;step0 >>= 1)// 2
{
for (int j = 0; j < step; j += step0<<1)//3
{
for (int k = 0; k < step0; ++k)//4
{
if (segdata[i+j+k] > segdata[i+j+k+step0]||segdata[i+j+k]!=segdata[i+j+k])  //交换数据使升序排列,同时判断二者之中是否有NaN
{
//交换data
float T=segdata[i+j+k];
segdata[i+j+k]=segdata[i+j+k+step0];
segdata[i+j+k+step0]=T;
}
}
}
}

if (i + step < len)
{  //////内部循环可任意交换
for (int step0= step>>1;step0 >0 ;step0 >>= 1) //1
{
for (int j = 0; j < step; j += step0<<1) //2
{
for (int k = 0; k < step0; ++k) //3
{
if (segdata[i + step+j + k] < segdata[i + step+j + step0 +k]
||segdata[i + step+j + step0 +k]!=segdata[i + step+j + step0 +k]) //交换数据使降序排列,同时判断二者之中是否有NaN
{
//交换data
float T=segdata[i+step+j+k];
segdata[i+step+j+k]=segdata[i+step+j+k+step0];
segdata[i+step+j+k+step0]=T;
}
}
}
}
}
}
}
for (int i = seg_start[d]; i < seg_start[d+1]; i++)
{
data[i]=segdata[i-seg_start[d]];
if(data[i]==Max)
data[i]=sqrt(-1.f);
}
}
}
void main()
{
float data[11]={ 0,sqrt(-1.f) -100 , 2, 100, 4, 0.5,sqrt(-1.f), sqrt(-1.f), 3, 0.1, 2};
int seg_id[11]={0, 0, 0, 1, 1, 2, 2, 2, 2,3,3};
int seg_start[5]={0,3,5,9,11};
int n=11;
int m=4;

//调用分段双调函数
segmentedBitonicSort(data,seg_id,seg_start,n,m);
//输出

for (int i = 0; i < n;++i) {
cout << data[i] <<"\t";
}
cout << endl;
}


C++实现分段双调排序算法

时间:2015年8月27日
目录

1.问题说明:...2

2.双调排序算法描述...4

3.尝试过和完成了的加分挑战...5

4.可以独立运行的源代码...6

5.测试数据...6

5.1测试1 样例...6

5.2测试2 输入数据中包含NaN..6

6.性能分析...6

7.测试的起始和完成时间以及实际使用的时间。...7

8.参考文献...8

致老师...9

1.问题说明:

***********************

给出分成m段 的n个浮点数,输入数据已按段号有序,但每段内部 无序。用C/C++ 编写一个分段双调排序(Bitonicsort)函数,对每一段内部的浮点数进行排序,但不要改变段间的位置。

接口方式:

***********************

void segmentedBitonicSort(float*data, int* seg_id, int* seg_start, int n, int m);

输入数据中,data包含需要分段排序的n个float值,seg_id给出data中n个元素各 自所在的 段编号。seg_start共有m+1个元素,前m个分别给 出0..m-1共m个段的起始位置,seg_start[m]保证等于n。

seg_id中的元素保证单调不下降,即对任意的i<j,seg_id[i]<=seg_id[j]。

seg_id所有元 素均在0到m-1范围内。

输出结果覆盖data,保证每一段内排序,但不改变段间元素的顺序。

注意:

***********************

1、必须使用双调排序算法进行排序。

2、可以直接使用从网上下载的双调排序代码,但须注明出处。

样例输入:

***********************

floatdata[5]={0.8, 0.2, 0.4, 0.6, 0.5};

intseg_id[5]={0, 0, 1, 1, 1}

intseg_start[3]={0,2,5};

int n=5;

int m=2;

样例输出:

***********************

floatdata[5]={0.2, 0.8, 0.4, 0.5, 0.6};

加分挑战(非必需):

***********************

1、不递归:segmentedBitonicSort函数及其所调用的任何其他函数都不得直接或间接地进行递归。

2、不调用函数:segmentedBitonicSort不调用除标准库函数外的任何其他函数。

3、内存高效:segmentedBitonicSort及其所调用的任何其他函数都不得进行动态内存分配,包括malloc、new和静态定义的STL容器。

4、可并行:segmentedBitonicSort涉及到的所有时间复杂度O(n)以上的代码都写在for循环中,而且每个这样的for循环内部的循环顺序可以任意改变,不影响程序结果。注:自己测试时可以用rand()决定循环顺序。

5、不需内存:segmentedBitonicSort不调用任何函数(包括C/C++标准库函数),不使用全局变量,所有局部变量都是int、float或指针类 型,C++程序不使用new关键字。

6、绝对鲁棒:在输入数据中包含NaN时(例如sqrt(-1.f)),保证除NaN以外 的数据正确排序,NaN的个数保持不变。

2.双调排序算法描述

所谓双调序列[1](Bitonic Sequence)是指由一个非严格增序列X和非严格减序列Y构成的序列,比如序列(23,10,8,3,5,7,11,78)。

定义:一个序列a1,a2,…,an是双调序列(Bitonic Sequence),如果:

(1)存在一个ak(1≤k≤n), 使得a1≥…≥ak≤…≤an成立;或者

(2)序列能够循环移位满足条件(1)

双调归并网络是基于Batcher定理而构建的。Batcher定理是说将任意一个长为2n的双调序列A分为等长的两半X和Y,将X中的元素与Y中的元素一一按原序比较,即a[i]与a[i+n](i<n)比较,将较大者放入MAX序列,较小者放入MIN序列。则得到的MAX和MIN序列仍然是双调序列,并且MAX序列中的任意一个元素不小于MIN序列中的任意一个元素。

根据这个原理,我们可以将一个输入的n元素双调序列首先通过洗牌比较操作得到一个MAX序列和一个MIN序列,然后通过两个n/2阶双调归并器处理就可以得到一个有序序列。

双调排序的直观图如下所示。



图 双调排序网络n=16[2]
经典的双调排序网络的设计是用来解决输入长度n=2^k的情况。对于任意n的双调排序网络算法,可采用如下方法:

当正向排序时,在数列后加(p-n)个远大于待排序数列的数,当逆向排序时,在数列后加(p-n)个远小于待排序数列的数,其中p是大于n的最小的2的幂次方,补充的数列不会对结果造成影响。

在本程序中,因为只在段内进行排序,不改变段间位置,所以本题有两种思路:1)直接进行段内排序,然后拼接在一起;2)先对整个大数组进行排序,再根据段的编号对数组进行分段。

在进行任意大小数组双调排序时,本程序采用的方法为:当n!=2^k时,在数列后加(p-n)个远大于待排序数列的数, p是大于n的最小的2的幂次方,使得新的数组是2的整数次幂,再对新的数组进行标准双调排序。补充的数列不会对结果造成影响,因为最后结果是采用升序排列。

3.尝试过和完成了的加分挑战

均有尝试。

完成挑战:

1、不递归:本程序无任何递归。

2、不调用函数:segmentedBitonicSort没有调用任何其他函数。

3、内存高效:segmentedBitonicSort函数没有进行动态内存分配,包括malloc、new和静态定义的STL容器。

4、可并行:segmentedBitonicSort涉及到的所有时间复杂度O(n)以上的代码都写在for循环中,而且每个这样的for循环内部的循环顺序可以任意改变,不影响程序结果。

5、不需内存:segmentedBitonicSort没有调用任何函数(包括C/C++标准库函数),所有局部变量都是int、float或指针类型,没有使用全局变量,C++程序不使用new关键字。

6、绝对鲁棒:在输入数据中包含NaN时(例如sqrt(-1.f)),保证除NaN以外的数据正确排序,NaN的个数保持不变。

说明:用NaN!=NaN,判断一个数是否为NaN,如果是NaN把它看作比任意数大的数。(第一次提交的测试有误,我碰巧得对了)

4.可以独立运行的源代码

运行环境:VS2012 Win32控制台应用程序。

源代码在附件中Bitonic_sort.cpp.

5.测试数据

5.1测试1 样例

输入:

float data[5]={0.8, 0.2, 0.4, 0.6, 0.5};

int seg_id[5]={0, 0, 1, 1, 1};

int seg_start[3]={0,2,5};

int n=5;

int m=2;

输出:


5.2测试2 输入数据中包含NaN

float data[11]={ 0,sqrt(-1.f) -100 , 2,100, 4, 0.5,sqrt(-1.f), sqrt(-1.f), 3, 0.1, 2};

int seg_id[11]={0, 0, 0, 1, 1, 2, 2, 2,2,3,3};

int seg_start[5]={0,3,5,9,11};

int n=11;

int m=4;

输出:


6.性能分析

时间复杂度:双调排序的时间复杂度是O(n(log n)^2),较理想网络排序O(n log n)略有不足。对于本题的两种思路:1)直接进行段内排序,然后拼接在一起;2)先对整个大数组进行排序,再根据段的编号对数组进行分段。

对于思路1平均时间复杂度为: ,当m越大复杂度越低。对于思路2,时间复杂度为O(len(loglen)^2),len=2^(log n+1),可得复杂度仍为O(n(logn)^2)。

双调排序算法是一种并行算法,假如有机器可以同时处理多个比较器,排序的速度将大幅度提高。

本程序中未使用递归,所以更节省时间和空间。内存高效,没有调用任何函数(包括C/C++标准库函数),没有使用全局变量,没有进行动态内存分配。可并行,segmentedBitonicSort涉及到的所有时间复杂度O(n)以上的代码都写在for循 环中,而且每个这样的for循环内部的循环顺序可以任意改变,不影响程序结果。绝对鲁棒,在输入数据中包含NaN时(例如sqrt(-1.f)),把NaN当作比任意数大的数进行排序,保证除NaN以外 的数据正确排序,NaN的个数保持不变。

不足:

1)本程序算法的复杂度较高,执行效率低;

2)对于不满足n=2^k的数组,采用补充远大于待排序数组的数字,这样就对待排数据的大小有了要求。

3)第二次的程序去掉了vector静态定义的STL容器,采用定义一个长度远大于待排数组的数组,这样就对待排数组的长度有了限制。

7.测试的起始和完成时间以及实际使用的时间。

开始测试时间:2015年8月26日9:30左右;

完成时间:2015年8月27日21时左右(发邮件时间);

实际使用时间:约18h左右;

接到题目,我就开始了查资料,我没有接触过双调排序,开始查资料,写程序。从开始测试到标准双调排序结果出来用来约2h,为了任意数列的双调排序大概又用了4h,期间也不断挑战着一些加分项,所以初次得出的任意数列的双调排序的程序和终版已无大的差别。为了挑战加分项,继续奋战了4h左右,放弃。整理文档约4h,这个没有特别计算,且分布零散。

8月27日11时左右第一次提交,得到老师回复。下午一直没有动程序,查了些资料,后有事出去,没有什么进度。晚上18时左右开始完善程序,因为没有做什么实质性的的修改,所以很快完成了。第二次查资料约1h,编程约1h,整理文档2h。

8.参考文献

[1]百度百科.双调排序.[EB/OL].
http://baike.baidu.com/link?url=eup59YUTZDGkAVKAXjKP3tmjCnZ8Uo4GoJ7o-bb6W8ns01PffLhUwtEz8jz2u3OY1mwZyo0uW3fuS9DjGb1Ltq
[2]Parallel Computing. Bitonic Sort.[EB/OL].
http://www.cs.rutgers.edu/~venugopa/parallel_summer2012/bitonic_overview.html

致老师

尊敬的老师,

您好!非常感谢您能给我这样一个机会。时间很短,学到的很多。

非常感谢您指出我的程序中的一些问题,并再次给我机会修改。经过再次编写程序,我也发现了很多漏洞和不足,有些已经改过来了。针对降低复杂度的问题,查阅的资料中有并行做标记的方法,因为网上查的资料不全,叙述不清,我对并行处理的了解不深,所以有些难度。在剩下的时间内我很难消化掉并写出代码。但是,我之后还会继续关注的。

我的程序一定还存在很多缺点、漏洞,请您斧正。如果有更好的解决方案,您能否发给我一份?我会不断学习!谢谢!

此致

敬礼!

2015年8月27日
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: