如何给100亿个数字排序?

作者: ck2016 | 来源:发表于2016-09-29 18:11 被阅读7465次

    场景

    之前写过一篇海量数据中统计ip出现次数最多的博客,今天再写篇类似的,当然会有不同的地方,相同的地方我快速写过,详细的可以看之前的博客

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

    实现思路

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

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

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

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

    最后我用c++写了个实验程序,具体代码在这里可以看到。

    思维拓展

    类似的100亿个数字求和,求中位数,求平均数,套路就是一样的了。
    求和:统计每个小文件的和,返回给master再求和就可以了。
    求平均数:上面能求和了,再除以100亿就是平均数了
    求中位数:在排序的基础上,遍历到中间的那个数就是中位数了。


    更正,评论中网友马敬v,提出了不用对数字进行哈希,直接平均分成1000份,进行内部排序后,直接进行 k 路归并排序,也是可以的。

    相关文章

      网友评论

      • 马敬v:为什么不直接平均分为1000个文件,而要进行哈希呢?
        ck2016:@马敬v 对,不用哈希,直接平均分1000份也是可以的
        马敬v: @ck2016 这样对后面的多路堆排序有什么用吗?相同数字在不同文件对效率有影响吗?
        ck2016:@马敬v 平均分的话,相同的数字可能去到不同的文件,哈希的话相同的元素,哈希后的值是一样的,就可以去到相同的文件里
      • 064abe403c77:其实可以不用拆分,建议楼主先了解下位图结构。
        ck2016:@nathanwhy 那我怎么排序,如果有100个重复的数字123, 放进bitmap后,只有1个了
        064abe403c77:@ck2016 位图中,一个字节可以放8个数字,1G内存可以放 80 亿个数字。
        ck2016:@nathanwhy 还是要拆分的
      • 71739aed572c:之前有个类似的笔记题,被虐惨了。楼主有没有例子和代码,让大家看看具体的实现和算法的性能?
        ck2016:@逗比1992 点击文章中的链接就可以了,蓝色字体的部分
        ck2016:@sebeeven 我更新一下文章
        ck2016:@sebeeven 有啊
      • ddb15933f32d:为什么是对1000取模呢
        ck2016:@你爸是我 我用1000只是举个例子,根据实际情况用不同的
      • 勤劳的小盛:Arrays.sort(Arr)
        ck2016:@精益阅读 也可以,调用系统的排序,如果想要更快,可以自己写个比系统快的
      • 阿狸卤鸭1984:文件怎么拆分的?
        ck2016:@阿狸卤鸭1984 一个32G的大文件,用fopen()打开不会全部加载到内存的,然后for循环遍历啊,把每个数字对1000取模,会得到0到999种结果,然后每种结果在写入到新的文件中,就拆分了
      • 小虫巨蟹:文件是怎么分拆的
        ck2016:@小虫巨蟹 有这个可能,这个叫数据倾斜,大量的数字落到了某个文件里,解决办法可以重新选取哈希函数,据说对素数取模比较均匀,或者拆分成1万个等等办法
        小虫巨蟹:@ck2016 有没有可能大多数数字都落在9或者别的哈希值上,导致分布不均,拆分之后还是很大
        ck2016:@小虫巨蟹 一个32G的大文件,用fopen()打开不会全部加载到内存的,然后for循环遍历啊,把每个数字对1000取模,会得到0到999种结果,然后每种结果在写入到新的文件中,就拆分了

      本文标题:如何给100亿个数字排序?

      本文链接:https://www.haomeiwen.com/subject/kjzcyttx.html