转自:http://netsmell.com/post/how-sort-10-billion-data.html?ref=myread

海量数据处理/外部归并排序 - 分治.cppp

今天要给100亿个数字排序,100亿个 int 型数字放在文件里面大概有 37.2GB,非常大,内存一次装不下了。那么肯定是要拆分成小的文件一个一个来处理,最终在合并成一个排好序的大文件。

实现思路

1.把这个37GB的大文件,用哈希分成1000个小文件,每个小文件平均38MB左右(理想情况),把100亿个数字对1000取模,模出来的结果在0到999之间,每个结果对应一个文件,所以我这里取的哈希函数是 h = x % 1000,哈希函数取得”好”,能使冲突减小,结果分布均匀。

2.拆分完了之后,得到一些几十MB的小文件,那么就可以放进内存里排序了,可以用快速排序,归并排序,堆排序等等。

3.1000个小文件内部排好序之后,就要把这些内部有序的小文件,合并成一个大的文件,可以用二叉堆来做1000路合并的操作,每个小文件是一路,合并后的大文件仍然有序。

首先遍历1000个文件,每个文件里面取第一个数字,组成 (数字, 文件号) 这样的组合加入到堆里(假设是从小到大排序,用小顶堆),遍历完后堆里有1000个 (数字,文件号) 这样的元素
然后不断从堆顶拿元素出来,每拿出一个元素,把它的文件号读取出来,然后去对应的文件里,加一个元素进入堆,直到那个文件被读取完。拿出来的元素当然追加到最终结果的文件里。
按照上面的操作,直到堆被取空了,此时最终结果文件里的全部数字就是有序的了。
最后我用c++写了个实验程序,具体代码在这里可以看到。

如何拆分大文件?

一个32G的大文件,用fopen()打开不会全部加载到内存的,然后for循环遍历啊,把每个数字对1000取模,会得到0到999种结果,然后每种结果在写入到新的文件中,就拆分了

// 对 2 亿个数字进行排序, 约 10 G 的文件, 每个数字 int 能表示
  3 // 算法流程
  4 // 将 10 G 的文件散列到 300 个文件中, 每个文件大约 35 MB
  5 // 对 35 MB 的小文件内部排序, 或者分发到多台计算机中, 并行处理 MapReduce
  6 // 最后使用最小堆, 进行 300 路归并排序, 合成大文件
  7 // 再写一个算法判断 2 亿个数字是否有序
  8  
  9 #include <stdio.h>
  10 #include <stdlib.h>
  11 #include <time.h>
  12 #include <io.h>
  13 #include <queue>
  14  
  15 #define FILE_NUM 300 // 哈希文件数
  16 #define HASH(a) (a % FILE_NUM)
  17  
  18 int num = 6000000; // 2 亿个数字, 手动改
  19 char path[20] = "c:\\data.dat"; // 待排文件
  20 char result[20] = "c:\\result.dat"; // 排序后文件
  21 char tmpdir[100] = "c:\\hashfile"; // 临时目录
  22  
  23 // 随机生成 2 亿个数字
  24 int write_file(void)
  25 {
  26   FILE *out = NULL;
  27   int i;
  28  
  29   printf("\n正在生成 %d 个数字...\n\n", num);
  30   out = fopen(path, "wt");
  31   if (out == NULL) return 0;
  32  
  33   unsigned int s, e;
  34   e = s = clock();
  35   for (i=0; i<num; i++)
  36   {
  37     e = clock();
  38     if (e - s > 1000) // 计算进度
  39     {
  40       printf("\r处理进度 %0.2f %%\t", (i * 100.0) / num);
  41       s = e;
  42     }
  43     fprintf(out, "%d\n",
  44         (rand() % 31623) * (rand() % 31623));
  45   }
  46   fclose(out);
  47   return 1;
  48 }
  49  
  50 // 对 2 亿个数字进行哈希, 分散到子文件中
  51 // 入口参数: path, tmpdir
  52 int map(void)
  53 {
  54   FILE *in = NULL;
  55   FILE *tmp[FILE_NUM + 5];
  56   char hashfile[512]; // 哈希文件地址
  57   int data, add;
  58   int i;
  59  
  60   printf("\r正在哈希 %s\n\n", path);
  61   in = fopen(path, "rt");
  62   if (in == NULL) return 0;
  63   for (i=0; i<FILE_NUM; i++) tmp[i] = NULL;
  64  
  65   // 开始哈希, 核心代码要尽可能的加速
  66   unsigned int s, e;
  67   e = s = clock();
  68   i = 0;
  69   while (fscanf(in, "%d", &data) != EOF)
  70   {
  71     add = HASH(data);
  72     if (tmp[add] == NULL)
  73     {
  74       sprintf(hashfile, "%s\\hash_%d.~tmp", tmpdir, add);
  75       tmp[add] = fopen(hashfile, "a");
  76     }
  77     fprintf(tmp[add], "%d\n", data);
  78  
  79     i++;
  80     e = clock(); // 计算进度
  81     if (e - s > 1000)
  82     {
  83       printf("\r处理进度 %0.2f %%\t", (i * 100.0) / num);
  84       s = e;
  85     }
  86   }
  87   for (i=0; i<FILE_NUM; i++)
  88   if (tmp[i]) fclose(tmp[i]);
  89   fclose(in);
  90  
  91   return 1;
  92 }
  93  
  94 // 对 300 个文件逐个排序, 采用堆排序 STL 的优先队列
  95 void calc(void)
  96 {
  97   int fileexist(char *path); // 判断文件存在
  98   std::priority_queue<int> q; // 堆排序
  99   char hashfile[512];
  100   FILE *fp = NULL;
  101   int i, data;
  102  
  103   // 逐个处理 300 个文件, 或者将这些文件发送到其它计算机中并行处理
  104   for (i=0; i<FILE_NUM; i++)
  105   {
  106     sprintf(hashfile, "%s\\hash_%d.~tmp", tmpdir, i);
  107     if (fileexist(hashfile))
  108     {
  109       printf("\r正在排序 hash_%d.~tmp\t", i);
  110  
  111       // 小文件从磁盘加入内存中
  112       fp = fopen(hashfile, "rt");
  113       while (fscanf(fp, "%d", &data) != EOF)
  114       {
  115         q.push(data);
  116         // 优先队列默认是大顶堆, 即降序排序
  117         // 要升序需要重载 () 运算符
  118       }
  119       fclose(fp);
  120  
  121       // 排序后再从内存写回磁盘
  122       fp = fopen(hashfile, "wt"); // 覆盖模式写
  123       while (!q.empty())
  124       {
  125         fprintf(fp, "%d\n", q.top());
  126         q.pop();
  127       }
  128       fclose(fp);
  129     }
  130   }
  131 }
  132  
  133 typedef struct node // 队列结点
  134 {
  135   int data;
  136   int id; // 哈希文件的编号
  137   bool operator < (const node &a) const
  138   { return data < a.data; }
  139 }node;
  140  
  141 // 将 300 个有序文件合并成一个文件, K 路归并排序
  142 int reduce(void)
  143 {
  144   int fileexist(char *path);
  145   std::priority_queue<node> q; // 堆排序
  146   FILE *file[FILE_NUM + 5];
  147   FILE *out = NULL;
  148   char hashfile[512];
  149   node tmp, p;
  150   int i, count = 0;
  151  
  152   printf("\r正在合并 %s\n\n", result);
  153   out = fopen(result, "wt");
  154   if (out == NULL) return 0;
  155   for (i=0; i<FILE_NUM; i++) file[i] = NULL;
  156   for (i=0; i<FILE_NUM; i++) // 打开全部哈希文件
  157   {
  158     sprintf(hashfile, "%s\\hash_%d.~tmp", tmpdir, i);
  159     if (fileexist(hashfile))
  160     {
  161       file[i] = fopen(hashfile, "rt");
  162       fscanf(file[i], "%d", &tmp.data);
  163       tmp.id = i;
  164       q.push(tmp); // 初始化队列
  165       count++; // 计数器
  166       printf("\r入队进度 %0.2f %%\t", (count * 100.0) / FILE_NUM);
  167     }
  168   }
  169   unsigned int s, e;
  170   e = s = clock();
  171   while (!q.empty()) // 开始 K 路归并
  172   {
  173     tmp = q.top();
  174     q.pop();
  175     // 将堆顶的元素写回磁盘, 再从磁盘中拿一个到内存
  176     fprintf(out, "%d\n", tmp.data);
  177     if (fscanf(file[tmp.id], "%d", &p.data) != EOF)
  178     {
  179       p.id = tmp.id;
  180       q.push(p);
  181       count++;
  182     }
  183  
  184     e = clock(); // 计算进度
  185     if (e - s > 1000)
  186     {
  187       printf("\r处理进度 %0.2f %%\t", (count * 100.0) / num);
  188       s = e;
  189     }
  190   }
  191   for (i=0; i<FILE_NUM; i++)
  192   if (file[i]) fclose(file[i]);
  193   fclose(out);
  194  
  195   return 1;
  196 }
  197  
  198 int check(void) // 检查是否降序排序
  199 {
  200   FILE *in = NULL;
  201   int max = 0x7FFFFFFF;
  202   int data;
  203   int count = 0;
  204  
  205   printf("\r正在检查文件正确性...\n\n");
  206   in = fopen(result, "rt");
  207   if (in == NULL) return 0;
  208  
  209   unsigned int s, e;
  210   e = s = clock();
  211   while (fscanf(in, "%d", &data) != EOF)
  212   {
  213     if (data <= max) max = data;
  214     else
  215     {
  216       fclose(in);
  217       return 0;
  218     }
  219     count++;
  220     e = clock(); // 计算进度
  221     if (e - s > 1000)
  222     {
  223       printf("\r处理进度 %0.2f %%\t", (count * 100.0) / num);
  224       s = e;
  225     }
  226   }
  227   fclose(in);
  228   return 1;
  229 }
  230  
  231 // 判断文件存在
  232 int fileexist(char *path)
  233 {
  234   FILE *fp = NULL;
  235  
  236   fp = fopen(path, "rt");
  237   if (fp)
  238   {
  239     fclose(fp);
  240     return 1;
  241   }
  242   else return 0;
  243 }
  244  
  245 int main(void)
  246 {
  247   char cmd_del[200]; // 删除目录
  248   char cmd_att[200]; // 设置隐藏
  249   char cmd_mkdir[200]; // 建立目录
  250  
  251   // 初始化 cmd 命令, 建立工作目录
  252   sprintf(cmd_del, "rmdir /s /q %s", tmpdir);
  253   sprintf(cmd_att, "attrib +h %s", tmpdir);
  254   sprintf(cmd_mkdir, "mkdir %s", tmpdir);
  255   if (access(path, 0) == 0) system(cmd_del);
  256   system(cmd_mkdir); // 建立工作目录
  257   system(cmd_att); // 隐藏目录
  258  
  259   // 随机生成 2 亿个数字
  260   if (!write_file()) return 0;
  261  
  262   map(); // 对 2 亿个数字进行哈希, 即 Map
  263   calc(); // 对 300 个文件逐个排序
  264   reduce(); // 最后将 300 个有序文件合并成一个文件, 即 reduce
  265   if (check()) printf("\r排序正确!\t\t\t\n\n");
  266   else printf("\r排序错误!\t\t\t\n\n");
  267  
  268   system(cmd_del); // 删除哈希文件
  269   remove(path); // 删除 2 亿数字文件
  270   remove(result); // 删除排序后的文件
  271  
  272   return 0;
  273 }

转载于:https://www.cnblogs.com/zhangxuan/p/5948291.html

如何给100亿个数字排序相关推荐

  1. 【算法】如何给100亿个数字排序?

    场景 之前写过一篇海量数据中统计ip出现次数最多的博客,今天再写篇类似的,当然会有不同的地方,相同的地方我快速写过,详细的可以看之前的博客. 今天要给100亿个数字排序,100亿个 int 型数字放在 ...

  2. 编写一个程序,从10亿个数字的数组中找出100个最大的数字

    本文翻译自:Write a program to find 100 largest numbers out of an array of 1 billion numbers I recently at ...

  3. 从10亿个数字中找出最大的前100个数

    先拿10000个数建堆,然后一次添加剩余元素,如果大于堆顶的数(10000中最小的),将这个数替换堆顶,并调整结构使之仍然是一个最小堆,这样,遍历完后,堆中的10000个数就是所需的最大的10000个 ...

  4. 100亿数据找出最大的1000个数字的4种方法

    100亿数据找出最大的1000个数字 面试题:解决的四种方法: 方法一:全排序法 1.最容易想到的方法是将数据全部排序.该方法并不高效,因为题目的目的是寻找出最大的10000个数即可,而排序却是将所有 ...

  5. 100亿数据找出最大的1000个数字

    这是互联网领域一个比较经典的算法问题(top k),如何在巨大的数据中找出最大,或者访问量最高的前10个,前100个或者前1000个数据.比如在2亿用户记录中找出信用等级最高的,在上亿个搜索词汇中找出 ...

  6. CCF201503-2 数字排序(100分)

    试题编号: 201503-2 试题名称: 数字排序 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序输出. 输 ...

  7. 《2020 数字中国指数报告》重磅发布,汤道生宣布将投入 100 亿用于开发中小企业专属 SaaS 产品及方案...

    整理 | 夕颜 出品 | CSDN(IDI:CSDNnews) 9月10日,在2020腾讯全球数字生态大会上,腾讯研究院联合腾讯云发布了<数字中国指数报告(2020)>(以下简称" ...

  8. 互联网晚报 | 12月20日 星期一 | 安踏100亿成立“和敏基金会”;阿里公布碳中和目标;京东上线数字藏品交易平台...

    今日看点 ✦ 阿里公布碳中和目标:2030年自身运营碳中和,带动生态15年减碳15亿吨 ✦ 华为推出"亚米级"高精度定位服务:助力实现真正车道级导航 ✦ 安踏100亿成立" ...

  9. 超100家交易所高危漏洞导致50亿价值数字资产受威胁

    作者:阿甫哥哥 原文来自:超100家交易所高危漏洞导致50亿价值数字资产受威胁 近日,Punisher安全团队联合HELM区块链安全实验室根据蜜罐捕捉到了一枚交易所的0day.据分析,此0day是由于 ...

最新文章

  1. TI-DM8127:MCFW、ISS中对sensor的驱动和控制
  2. 冒泡排序(O(n^2))
  3. 采样算法哪家强?一个针对主流采样算法的比较
  4. h5jumppage.php,h5使用webviewjsbridge跟原生交互,点击标签调用2次webview
  5. mongodb mysql并发_MongoDB:锁和并发控制
  6. Datatables参数详解
  7. jquery 使用textarea
  8. linux命令大全_Linux 系统下查看硬件信息命令大全
  9. lstrip在python中是什么意思_为什么氦气吸入后会变声?
  10. oracle用户导出和导入用不同的用户名,oracle用exp\imp导出导入,及创建表空间与用户...
  11. 游戏筑基开发之学生管理系统数据结构及数据结构解析图(C语言)
  12. Dedecms之SQL语句修改和调用数据总结
  13. Rviz教程系列第一章之Markers
  14. Rediskey的基本操作
  15. 牛客寒假训练营1 K 冒险公社(线性dp)
  16. VMWare 导出vmdk并转为qcow2格式
  17. C# WebApi 返回详细错误信息
  18. 逆向知识之CS辅助/外挂专题.2.实现CS1.6无限夜视仪.无限闪光烟雾高爆弹.
  19. android混淆!原生Android开发的路该怎么走?技术详细介绍
  20. 教你前端如何用js写一个跑酷小游戏

热门文章

  1. 两个Activity左右滑动手势切换
  2. 看看一个朋友写的代码,大家发表发表意见,比较简单的代码
  3. vue:axios二次封装,接口统一存放
  4. [Java] 蓝桥杯ALGO-48 算法训练 关联矩阵
  5. FTP服务器软件 虚拟目录,FTP服务器软件 虚拟目录
  6. java poi读取excel日期格式数据
  7. 解决eclipse配置Tomcat时找不到server选项
  8. @ARGV:perl命令行参数
  9. POJ1236:Network of Schools——题解
  10. 字段与属性的总结与比较