zoukankan      html  css  js  c++  java
  • 【海量数据处理】100亿个整数,内存足够,如何找到中位数?内存不足,如何找到中位数?

    内存足够的情况: 可以使⽤用类似quick sort的思想进行,均摊复杂度为O(n),算法思想如下: 
    • 随机选取一个元素,将比它小的元素放在它左边,比它大的元素放在右边 
    • 如果它恰好在中位数的位置,那么它就是中位数,可以直接返回 
    • 如果小于它的数超过一半,那么中位数一定在左半边,递归到左边处理 
    • 否则,中位数一定在右半边,根据左半边的元素个数计算出中位数是右半边的第几大,然后递归 到右半边处理 


    内存不足的情况:

    无重复数字:

      bitmap方法

    有重复数字:

      既然要找中位数,很简单就是排序的想法。那么基于字节的桶排序是一个可行的方法 :

      思想:将整形的每1byte作为一个关键字,也就是说一个整形可以拆成4个keys,而且最高位的keys越大,整数越大。如果高位keys相同,则比较次高位的keys。整个比较过程类似于字符串的字典序。

      第一步:把10G整数每2G读入一次内存,然后一次遍历这536,870,912个数据。每个数据用位运算">>"取出最高8位(31-24)。这8bits(0-255)最多表示255个桶,那么可以根据8bit的值来确定丢入第几个桶。最后把每个桶写入一个磁盘文件中,同时在内存中统计每个桶内数据的数量,自然这个数量只需要255个整形空间即可。

      代价:(1) 10G数据依次读入内存的IO代价(这个是无法避免的,CPU不能直接在磁盘上运算)。(2)在内存中遍历536,870,912个数据,这是一个O(n)的线性时间复杂度。(3)把255个桶写会到255个磁盘文件空间中,这个代价是额外的,也就是多付出一倍的10G数据转移的时间。 

      第二步:根据内存中255个桶内的数量,计算中位数在第几个桶中。很显然,2,684,354,560个数中位数是第1,342,177,280个。假设前127个桶的数据量相加,发现少于1,342,177,280,把第128个桶数据量加上,大于1,342,177,280。说明,中位数必在磁盘的第128个桶中。而且在这个桶的第1,342,177,280-N(0-127)个数位上。N(0-127)表示前127个桶的数据量之和。然后把第128个文件中的整数读入内存。(平均而言,每个文件的大小估计在10G/128=80M左右,当然也不一定,但是超过2G的可能性很小)。

      代价:(1)循环计算255个桶中的数据量累加,需要O(M)的代价,其中m<255。(2)读入一个大概80M左右文件大小的IO代价。 

    注意,变态的情况下,这个需要读入的第128号文件仍然大于2G,那么整个读入仍然可以按照第一步分批来进行读取。 

      第三步:继续以内存中的整数的次高8bit进行桶排序(23-16)。过程和第一步相同,也是255个桶。

      第四步:一直下去,直到最低字节(7-0bit)的桶排序结束。我相信这个时候完全可以在内存中使用一次快排就可以了。

       整个过程的时间复杂度在O(n)的线性级别上(没有任何循环嵌套)。但主要时间消耗在第一步的第二次内存-磁盘数据交换上,即10G数据分255个文件写回磁盘上。一般而言,如果第二步过后,内存可以容纳下存在中位数的某一个文件的话,直接快排就可以了。

    参考资料:

      1. http://www.zhihu.com/question/35365929

      2. http://hxraid.iteye.com/blog/649831

  • 相关阅读:
    java基础知识回顾之final
    基础知识《十四》Java异常的栈轨迹fillInStackTrace和printStackTrace的用法
    基础知识《六》---Java集合类: Set、List、Map、Queue使用场景梳理
    基础知识《五》---Java多线程的常见陷阱
    基础知识《四》---Java多线程学习总结
    《转》如何选择合适的服务器托管商
    基础知识《三》java修饰符
    基础知识《零》---Java程序运行机制及运行过程
    应用 JD-Eclipse 插件实现 RFT 中 .class 文件的反向编译
    DOS命令符基本操作
  • 原文地址:https://www.cnblogs.com/vincently/p/4817011.html
Copyright © 2011-2022 走看看