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