C++实现分段双调排序算法
2015-10-14 22:12
459 查看
C++实现分段双调排序算法
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
给出分成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的个数保持不变。
定义:一个序列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的整数次幂,再对新的数组进行标准双调排序。补充的数列不会对结果造成影响,因为最后结果是采用升序排列。
完成挑战:
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把它看作比任意数大的数。(第一次提交的测试有误,我碰巧得对了)
源代码在附件中Bitonic_sort.cpp.
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;
输出:
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;
输出:
对于思路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容器,采用定义一个长度远大于待排数组的数组,这样就对待排数组的长度有了限制。
完成时间:2015年8月27日21时左右(发邮件时间);
实际使用时间:约18h左右;
接到题目,我就开始了查资料,我没有接触过双调排序,开始查资料,写程序。从开始测试到标准双调排序结果出来用来约2h,为了任意数列的双调排序大概又用了4h,期间也不断挑战着一些加分项,所以初次得出的任意数列的双调排序的程序和终版已无大的差别。为了挑战加分项,继续奋战了4h左右,放弃。整理文档约4h,这个没有特别计算,且分布零散。
8月27日11时左右第一次提交,得到老师回复。下午一直没有动程序,查了些资料,后有事出去,没有什么进度。晚上18时左右开始完善程序,因为没有做什么实质性的的修改,所以很快完成了。第二次查资料约1h,编程约1h,整理文档2h。
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日
//问题说明: // //给出分成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日
相关文章推荐
- c语言学习笔记(13)pragma详解,#和##运算符及编译指示字总结
- poco框架:日志相关知识和使用方法
- [C++] Memory Retrieval(内存检索)
- C语言中简单的for循环和浮点型变量
- 求一个字符串中最长的字符串
- More Effective C++ 条款35 让自己习惯于标准C++ 语言
- 【c语言】 小游戏——猜字游戏
- C++ 不改变链表结构 逆序打印
- C++:操作符
- [c++]使用前后缀表示常量的类型
- C++文件操作
- 【LeetCode从零单刷】Balanced Binary Tree
- C++基础编程----3.2多维数组
- More Effective C++ 条款34 如何在一个程序中结合C++和C
- C语言书写规范
- C++ 深拷贝与浅拷贝
- 【LeetCode从零单刷】Find Peak Element
- C++ 头文件cstring,string.h,string 三者的区别
- ARM裸机程序开发(十):C语言环境初始化
- 关于c语言中的字符数组的输入问题