关于海量topk问题,已经有无数文章讨论过,比如这里的程序员编程艺术:第三章续、Top K算法问题的实现或者算法探讨——Top K算法详细解析。
其问题本身比较容易,就是在一组海量数据中求得出现次数最多的k个数据。
对于这个问题,我们的思路应该如何较好呢?
简化问题,将k首先设置为1,如果要求最大的1个数据,该如何做?
首先,我们得使用一个数据结构保存每个数据出现的次数,因为经常性需要查找定位,所以最好的结构自然是HASH了
其次,每次定位时我们都采用一个额外的空间保存出现次数最大的那个数据。如此一来,当遍历完这组数据时,这个额外的空间便是出现次数最大的那个数据。
接下来,将数据个数推广到k,
如果采用k个额外空间保存出现次数最大的数据,那么,有什么比较好的算法?
再次简化问题,使用插入排序,每次用k个空间,过滤掉大于k的数据,这样就推广到了k,那么插入排序的时间复杂度如何呢?为O(nk),即遍历n次数据,每次最坏需要比较k次,如果采用小堆,则能够将复杂度降到O(nlogk)。
因而网上的最佳答案是HASH+堆。而对于各种变种,这里一篇文章已经讲得很清楚了教你如何迅速秒杀掉:99%的海量数据处理面试题。其中思路也比较值得借鉴。其基本思路就是:能够放到内存中的数据量,则采用bitmap,bloom filter等方法放到内存;不能放到内存中的数据,则分开为小文件或者小节点,每个小文件分别计算,最后汇总计算。
---------------------------------------------------------------分割线-----------------------------------------------------------------
但是有很大一部分数据处理元祖上的topk问题(或者称多属性的数据集,可以理解为数据库中的数据记录)
对于这类问题,有很多现成的算法,这里主要介绍下成熟的TA,NRA等
TA算法(threshold algorithm)是一种经典的topk算法,主要用于支持随机读取的环境。
如下例,算法开始先从第一行顺序扫描,计算出这行总的值大小2.7,并随机读取到所有列中a和b的值,计算并保存,由于最大值b=2.2 < t=2.7,因此算法继续;
第二行算出t大小2.5,并随机读取计算c和d的值,计算并保存,最大值b=2.2< t=2.5,因此算法继续;
第三行算出t大小2.1,随机读取计算出e的值,最大值仍是b=2.2 > t=2.1,因此算法结束,找到最大数值b
其形式化描述请参考论文《海量数据的快速查询算法研究》
然而,很多数据库是不能够随机读的,因此,作为补充,NRA(No random access)算法被提出来。
如图,仍使用上例中的数据。由于不能随机读,现在顺序读取每一行,计算出最好的可能跟最坏可能,分别用B和W表示,比如,第一行中a=0.9,因此最好的可能是B(a)=0.9*3,最坏可能W=0.9 + 0 + 0,同理可以算出B(b), W(b)。这时候需要计算Tk, Tk的定义是w最大的k个对象,假设k=1,则此处Tk={b}。
同理,继续计算后续行,直到w(b)>w(a)(因为b是Tk中最小的,其中k=1)