TopK问题

首先对于1亿的数据量,要求不高的话,大可只使用一个最小堆来维护前10万个最大的数。
不妨设数的总数为N,需要选出的数为M,那么这种方法带来的时间复杂度为:O(NlogM)
这个方法最大的缺点是没有并行计算

如果比1亿数据量再大,比如到100亿这个量级,那么就需要使用并行计算了,下面我详细说一下我的思路。

  1. 首先将这些数据随机分成K堆,不妨假设正好平均分配,时间复杂度为O(N)
  2. 使用K个任务并行的选出每一堆的前M大的数,这一步的时间复杂度为O(\frac{N}{K}logM) ,此时生成了K组长度为M的有序序列。
  3. 使用多路归并排序选出这K组序列的前M大的数,这一步的时间复杂度为O(MlogK)
    因此假设第2步并行完成,那么总体的时间复杂度就是O(N+\frac{N}{M}logM+MlogK)。至此算法得到了常数级别的优化。
    但是还没完,注意到2)中每个序列都选了前M大的数,这个是可以继续改进的。
    我们可以想象一种场景:2个桶里总共有10个球,球是随机分布的,规定一种操作可以从这2个桶里拿至多L个球,这时我们怎么确定这个L使得我们能够以一个我们能接受的概率拿到所有的球?
    显然当L=10的时候,我们当然可以100%的拿到这全部球,但是开销太大了。由于球是随机分布的,可以知道10个球全部落入落入一个盒子里的概率非常的小,是\frac{1}{2^9} 。因此我们可以先把这种请当当成一个中彩票的事件,先不管他,直接把L设成9。
    既然我们牺牲了一部分可靠性降低了开销,那么为什么不能把L设的在低一点?究竟应该设多低?
    这其实是另外的一个问题了,我认为为了解决这个问题不应该从L入手,而是我们设定一个最低能接受的可靠性概率P,找到最小的L满足我们要求的P即可。
    因此我们可以使用如上方法优化2)。设我们要求的最低可靠性为P, 优化函数为\varphi (K,M,P)
  4. 使用K个任务并行的选出每一堆的前\varphi (K,M,P)大的数,这一步的时间复杂度为O(\frac{N}{K} log\varphi (K,M,P)), 此时生成了K组长度为\varphi (K,M,P)的有序序列。
    我可以给出一个数\varphi (16,1000,0.999)\leq 94, 可以看出这个函数的威力。所以用这种方法,可以在实际中获得很多倍的速度提升。
    但是必须解决的问题是可靠性问题,但这个问题其实很好办,只需要验证一下在3)时有没有把一个序列用尽,如果用尽了并且最后一个数不是这个序列里的最后一个数,那么说明这个序列的第\varphi (K,M,P)+1个数也可能出现在最坏的结果。那么最简单的做法就是再从这个序列中补上一部分数据,使得最后的答案时正确的。
    所以这时我们得到了一个以P为成功率的算法,这个算法可能比未优化前快好多倍,但是有(1-P)的中奖概率。欣慰的是我们可以判断中没中奖,再不济就是抛弃这次的结果再跑一次未优化前的算法呗。