您的位置:首页 > 其它

桶排序用于海量数据排序的实验。

2011-06-14 18:13 246 查看
刚才在JULY的博客上看了对海量数据排序的讲解(http://blog.csdn.net/v_JULY_v/archive/2011/05/28/6451990.aspx),仔细看了几遍,大概理解了使用归并排序的执行过程。于是想知道如果把归并排序替换成其他方法是否可行。仔细想了一下,觉得桶排序可以一试,所写了程序测试了一下。

算法的思路是:

第一步。把每个数据最高的k位作为桶编号,创建2^k个桶.然后把每个数据加入到对应的桶中,因为内存有限,把每个桶与一个文件对应,这样可以得到2^k个文件,由于不同桶中的数据大小已经不同,所以只需要分别把每个桶中的数据排好,那么全部数据就是有序的了。

第二步。采用类似计数排序的方法分别对每个文件(桶)中的数据进行排序,由于内存有限不能直接使用计数排序,而是用计数排序的简化版。

先用一个数组统计每个数字出现的次数

arr[SIZE]={0};

for each d;

++arr[d];

然后根据数组中每个元素出现的次数由小到大地输出数据。

for each i in arr

num=arr[i];

while num!=0

print i; //输出结果

num=num-1;

这样就把一个文件中的数据排好了,分析上面的算法会发现它是 稳定 的排序算法。

分析数组arr的大小。

因为数据有10^7个,因此,arr中元素的类型至少应是int类型的,占4个字节。由于内存限制为1M 所以数组arr的大小可取到256k=2^18.

也就是说第一步的每个桶最多只能放2^18个不同的数。题目说每个数字最大为10^7=2^24,因此可以将每个数字的第18~23位作为桶编号,可得64个桶。又因为10^7的二进制形式的第18~23位是100110,转成十进制是38, 即最大的数落在编号为38的桶中。因此实际只需39个桶即可。

---------------------------------------------------------------------------------------

实验结果:

我在vs2010的release版下测试,所使用的数据是用JULY博客中的给出的随机数生成算法生成的。排序所花的总时间为39秒。

又在同样的环境下测试了一下JULY博客中的算法执行时间为:

The time needs in memory sort: 22172ms
The time needs in merge sort: 20344ms

这个算法是按两步分别计时的,所以总时间为42秒。

可见两个算法执行时间相似。

--------------------------------------------------------------------------------

源码:

#include <iostream>
#include <time.h>
#include <assert.h>
#include <string.h>
using namespace std;

const int size = 10000000;

#define LOW 18 	      //桶中数据的位数
#define FILE_NUM 39	   //桶对应的文件数

#define MEM_SIZE 256*1024

int memory[MEM_SIZE];     //1M

//对ifp中的数据进行排序,结果输出到ofp中
void sort(FILE*ifp, FILE *ofp)
{
memset(memory,0,1024*1024);

int d;
int high;			 //保存数据的高位

if(fscanf(ifp, "%d", &d)==1)   //处理第一个数
{
++memory[d&0x3ffff]; //计数,只是用低18位
high=d&0xfffc0000;   //保存高位

}

while(fscanf(ifp, "%d", &d)==1)  //处理其它的数
{

++memory[d&0x3ffff]; //计数,只是用低18位
}

for (int i=0; i<MEM_SIZE; ++i)
{
int num=memory[i];
while(num--)
{
fprintf(ofp,"%d ",i|high);	   //输出结果
}

}

}

int main()
{
FILE *fp_tmp[FILE_NUM];
FILE *fp_data;

if(NULL==(fp_data=fopen("d:/test/data.txt","r"))) //打开测试数据
exit(0);
int d;
int i;

time_t start = time(NULL);    //开始计时

for (i=0; i<FILE_NUM; ++i)	  //创建桶对应的FILE_NUM个文件
{
char buf[64]="d:/test/tmp_";
char buf_int[4];
itoa(i, buf_int, 10);
strcat(buf,buf_int);
strcat(buf,".txt");
if((fp_tmp[i]=fopen(buf,"w+"))==NULL)
exit(0);

}

while(fscanf(fp_data,"%d",&d)==1)	 //读入数据存放到各个桶中
{
fprintf(fp_tmp[d>>LOW], "%d ",d);

}
for (i=0; i<FILE_NUM; ++i)		 //初始化文件指针
{
rewind(fp_tmp[i]);

}

FILE * out_fp;
if(NULL==(out_fp=fopen("d:/test/out.txt","w")))	    //out.txt用于保存排序后的数据
exit(0);

for (i=0; i<FILE_NUM; ++i)
{
sort(fp_tmp[i],out_fp);		//分别对每个桶进行排序

}

for (i=0; i<FILE_NUM; ++i)	  //关闭文件
{
fclose(fp_tmp[i]);

}
time_t end = time(NULL);   	  //停止计时

printf("total time:%f/n", (end - start) * 1000.0/ CLOCKS_PER_SEC);

system("pause");
}


-----------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------

对上面的程序改进了一下,上面的算法保存桶中数据时,存的是整个数字,但实际上数据的高位是不用存的,同一个桶中的数据的高位都是相同的,根据桶的编号就可以把数据的高位部分确定出来。于是我对程序做了修改。

原来的桶文件大小约为2.0M,改进以后每个桶的大小为1.68M,执行时间也有所降低。

改进后的算法的执行时间为28秒。

又重复做了几次试验,发现所用时间均为33秒。

-------------------------------------

源码:

#define LOW 18 	      //桶大小
#define FILE_NUM 39	   //桶对应的文件数

#define MEM_SIZE 256*1024

int memory[MEM_SIZE];     //1M

//对ifp中的数据进行排序,结果输出到ofp中 ,i是正在处理的桶的编号
void sort(FILE*ifp, FILE *ofp, int i)
{
memset(memory,0,1024*1024);

int d;
int high=i<<LOW;			 //保存数据的高位

/*	if(fscanf(ifp, "%d", &d)==1)
{
++memory[d&0x3ffff]; //计数,只是用低18位
high=d&0xfffc0000;   //保存高位

}*/

while(fscanf(ifp, "%d", &d)==1)
{

++memory[d&0x3ffff]; //计数,不考虑高五位
}

for (int i=0; i<MEM_SIZE; ++i)
{
int num=memory[i];
while(num--)
{
fprintf(ofp,"%d ",i|high);	   //输出结果
}

}

}

int main()
{
FILE *fp_tmp[FILE_NUM];
FILE *fp_data;

if(NULL==(fp_data=fopen("d:/test/data.txt","r"))) //打开测试数据
exit(0);
int d;
int i;

time_t start = time(NULL);    //开始计时

for (i=0; i<FILE_NUM; ++i)	  //创建桶对应的FILE_NUM个文件
{
char buf[64]="d:/test/tmp_";
char buf_int[4];
itoa(i, buf_int, 10);
strcat(buf,buf_int);
strcat(buf,".txt");
if((fp_tmp[i]=fopen(buf,"w+"))==NULL)
exit(0);

}

while(fscanf(fp_data,"%d",&d)==1)	 //读入数据存放到各个桶中
{
fprintf(fp_tmp[d>>LOW], "%d ",d&0x3ffff);

}
for (i=0; i<FILE_NUM; ++i)		 //初始化文件指针
{
rewind(fp_tmp[i]);

}

FILE * out_fp;
if(NULL==(out_fp=fopen("d:/test/out.txt","w")))	    //out.txt用于保存排序后的数据
exit(0);

for (i=0; i<FILE_NUM; ++i)
{
sort(fp_tmp[i],out_fp,i);		//分别对每个桶进行排序

}

for (i=0; i<FILE_NUM; ++i)	  //关闭文件
{
fclose(fp_tmp[i]);

}
time_t end = time(NULL);   	  //停止计时

printf("total time:%f/n", (end - start) * 1000.0/ CLOCKS_PER_SEC);

system("pause");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: