今天要给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种结果,然后每种结果在写入到新的文件中就拆分了
|
|
|
|
// 最后使用最小堆, 进行 300 路归并排序, 合成大文件
|
// 再写一个算法判断 2 亿个数芓是否有序
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 随机生成 2 亿个数字
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对 2 亿个数字进行哈希, 分散到子文件中
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 开始哈希, 核心代码要尽可能的加速
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 对 300 个文件逐个排序, 采用堆排序 STL 的優先队列
|
|
|
|
|
|
|
|
|
// 逐个处理 300 个文件, 或者将这些文件发送到其它计算机中并行处理
|
|
|
|
|
|
|
|
// 小文件从磁盘加入内存中
|
|
|
|
|
// 优先隊列默认是大顶堆, 即降序排序
|
// 要升序需要重载 () 运算符
|
|
|
|
// 排序后再从内存写回磁盘
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将 300 个有序文件合并成一个文件, K 路归并排序
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将堆顶的元素写回磁盘, 再从磁盘中拿一个到内存
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化 cmd 命令, 建立工作目录
|
|
|
|
|
|
|
|
// 随机生成 2 亿个数字
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|