100亿数据找出最大的1000个数字(top K问题)

在大规模数据处理中,常常会遇到的一类问题:在海量数据中找出出现频率最好的前k个数,或者从海量数据中找出最大的前k个数,这类问题一般被称为top K问题。例如,在搜索引擎中,统计搜索最热门的10个查询词;在歌曲库中统计下载最高的前10首歌等。算法

一、最容易想到的方法是将数据所有排序。该方法并不高效,由于题目的目的是寻找出最大的10000个数便可,而排序倒是将全部的元素都排序了,作了不少的无用功。数组

二、局部淘汰法。用一个容器保存前10000个数,而后将剩余的全部数字一一与容器内的最小数字相比,若是全部后续的元素都比容器内的10000个数还小,那么容器内这个10000个数就是最大10000个数。若是某一后续元素比容器内最小数字大,则删掉容器内最小元素,并将该元素插入容器,最后遍历完这1亿个数,获得的结果容器中保存的数即为最终结果了。此时的时间复杂度为O(n+m^2),其中m为容器的大小。性能

这个容器能够用(小顶堆)最小堆来实现。咱们知道彻底二叉树有几个很是重要的特性,就是假如该二叉树中总共有N个节点,那么该二叉树的深度就是log2N,对于小顶堆来讲移动根元素到 底部或者移动底部元素到根部只须要log2N,相比N来讲时间复杂度优化太多了(1亿的logN值是26-27的一个浮点数)。基本的思路就是先从文件中取出1000个元素构建一个小顶堆数组k,而后依次对剩下的100亿-1000个数字进行遍历m,若是m大于小顶堆的根元素,即k[0],那么用m取代k[0],对新的数组进行从新构建组成一个新的小顶堆。这个算法的时间复杂度是O((100亿-1000)log(1000)),即O((N-M)logM),空间复杂度是M大数据

这个算法优势是性能尚可,空间复杂度低,IO读取比较频繁,对系统压力大。优化

三、第三种方法是分治法,即大数据里最经常使用的MapReduce。搜索引擎

a、将100亿个数据分为1000个大分区,每一个区1000万个数据排序

b、每一个大分区再细分红100个小分区。总共就有1000*100=10万个分区索引

c、计算每一个小分区上最大的1000个数。内存

为何要找出每一个分区上最大的1000个数?举个例子说明,全校高一有100个班,我想找出全校前10名的同窗,很傻的办法就是,把高一100个班的同窗成绩都取出来,做比较,这个比较数据量太大了。应该很容易想到,班里的第11名,不多是全校的前10名。也就是说,不是班里的前10名,就不多是全校的前10名。所以,只须要把每一个班里的前10取出来,做比较就好了,这样比较的数据量就大大地减小了。咱们要找的是100亿中的最大1000个数,因此每一个分区中的第1001个数必定不多是全部数据中的前1000个。容器

d、合并每一个大分区细分出来的小分区。每一个大分区有100个小分区,咱们已经找出了每一个小分区的前1000个数。将这100个分区的1000*100个数合并,找出每一个大分区的前1000个数。

e、合并大分区。咱们有1000个大分区,上一步已找出每一个大分区的前1000个数。咱们将这1000*1000个数合并,找出前1000.这1000个数就是全部数据中最大的1000个数。

(a、b、c为map阶段,d、e为reduce阶段)

四、Hash法。若是这1亿个书里面有不少重复的数,先经过Hash法,把这1亿个数字去重复,这样若是重复率很高的话,会减小很大的内存用量,从而缩小运算空间,而后经过分治法或最小堆法查找最大的10000个数。

对于海量数据处理,思路基本上是:必须分块处理,而后再合并起来。

相关文章
相关标签/搜索