桶排序用于海量数据排序的实验。
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秒。
可见两个算法执行时间相似。
--------------------------------------------------------------------------------
源码:
-----------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------
对上面的程序改进了一下,上面的算法保存桶中数据时,存的是整个数字,但实际上数据的高位是不用存的,同一个桶中的数据的高位都是相同的,根据桶的编号就可以把数据的高位部分确定出来。于是我对程序做了修改。
原来的桶文件大小约为2.0M,改进以后每个桶的大小为1.68M,执行时间也有所降低。
改进后的算法的执行时间为28秒。
又重复做了几次试验,发现所用时间均为33秒。
-------------------------------------
源码:
算法的思路是:
第一步。把每个数据最高的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"); }
相关文章推荐
- 暑假集训 8.16 数据结构实验之排序三:bucket sort (简单的桶排序)
- 海量数据排序总结
- 数据结构实验之排序一:一趟快排
- 数据结构实验之排序四:寻找大富翁
- 数据结构实验之排序二:交换排序
- 数据结构实验之排序四:寻找大富翁(堆排序)
- 【数据结构与算法】内部排序之五:计数排序、基数排序和桶排序(含完整源码)
- 桶排序解决字符串排序问题
- sdut 数据结构实验之排序五:归并求逆序数
- 数据结构实验之排序三:bucket sort
- 数据结构实验之排序六:希尔排序
- 数据结构实验之排序二:交换排序
- C++11新特性应用--介绍几个新增的便利算法(用于排序的几个算法)
- 数据结构实验之排序四:寻找大富翁——堆排序
- matlab用于试验设计回归分析实验结果的例子
- 数据结构实验之排序四:寻找大富翁
- 算法导论实验:第二章插入排序 P10
- 程序设计C 实验四 题目一 排序查找(0616)
- 实验4 输入四个人的年龄和姓名,排序后,按年龄,从小到大输出人员年龄及其姓名
- 实验三-查找与排序-5