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

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


 


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值