单遍算法寻找前X百分比项目

4 投票
4 回答
1072 浏览
提问于 2025-04-16 21:28

我在寻找一种算法,可以在不知道总共有多少个浮点数的情况下,从一个数据流中找到前X%的浮点数。这个数据流的浮点数大概在500万到3000万之间。因为数据是实时生成的,所以我需要一种单次遍历的算法,不能再重新生成一次数据流。

我现在想到的算法是保持一个已经排序好的列表,里面存放我目前看到的前X个浮点数。随着数据流的继续,我会根据需要扩大这个列表。然后,我会用bisect_left来找到插入点,如果需要的话。

下面是我目前的算法:

from bisect import bisect_left
from random import uniform
from itertools import islice


def data_gen(num):
    for _ in xrange(num):
        yield uniform(0,1)

def get_top_X_percent(iterable, percent = 0.01, min_guess = 1000):

    top_nums = sorted(list(islice(iterable, int(percent*min_guess)))) #get an initial guess

    for ind, val in enumerate(iterable, len(top_nums)):
        if int(percent*ind) > len(top_nums):
            top_nums.insert(0,None)
        newind = bisect_left(top_nums, val)
        if newind > 0:
            top_nums.insert(newind, val)
            top_nums.pop(0)

    return top_nums

if __name__ == '__main__':

    num = 1000000
    all_data = sorted(data_gen(num))
    result = get_top_X_percent(all_data)
    assert result[0] == all_data[-int(num*0.01)], 'Too far off, lowest num:%f' % result[0] 
    print result[0]

在实际情况下,这些数据并不是来自任何标准的分布(要是能的话,我就可以用一些统计知识了)。

任何建议都非常欢迎。

4 个回答

1

正如其他回答所提到的,存储整个数据流在内存中是比较有效的做法。考虑一下这种方式,毕竟5到3000万的浮点数大约只占用40到240MB的内存,这个大小是可以接受的。

既然你要存储整个数据流,那么最快的方法来找出前X%的数据,就是先找到一个分界值(也就是在前X%中最小的那个数),可以用一种线性时间的选择算法来实现:

http://en.wikipedia.org/wiki/Selection_algorithm

然后,再遍历一遍数据流,把所有比这个分界值小的数过滤掉。

这种方法的时间和空间复杂度都是线性的,这是你能得到的最好的效果。

4

必须把整个数据流都存到内存里。

证明:假设你有一串数字,n1,…,nk。这里的k是个未知数。你怎么知道什么时候可以忘掉 ni 这个数字呢?当你看到超过 ni 的数字有 x*k/100 个的时候。但是,因为k是未知的,所以你永远无法做到这一点。

所以,唯一的“单次遍历”算法必须把整个序列都存到内存里。

5

我不太确定有没有可靠的方法来做到这一点,因为“前X百分比”所代表的范围会随着你看到的元素数量而不可预测地变化。想象一下下面这个输入:

 101 102 103 104 105 106 107 108 109 110 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0...

如果你想要前25%的元素,你可能会从前十个元素中选出101和102,但当你看到足够多的零之后,最终你可能不得不选出前十个元素中的所有。这种情况可以扩展到任何足够大的数据流中——总是有可能因为表面现象而被误导,丢弃那些其实应该保留的元素。因此,除非你事先知道数据流的确切长度,否则我认为这是不可能的(除非你在数据流结束之前把每个元素都保存在内存中)。

撰写回答