Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 查找项目topX百分比的单次传递算法_Python_Algorithm - Fatal编程技术网

Python 查找项目topX百分比的单次传递算法

Python 查找项目topX百分比的单次传递算法,python,algorithm,Python,Algorithm,我正在寻找一种单遍算法,用于查找流中浮点数的topX百分比,在这种情况下,我事先不知道总浮点数。。。但其浮动量大约为500-3000万。它需要单次传递,因为数据是动态生成的,并且第二次重新创建准确的流 到目前为止,我所使用的算法是保存一个到目前为止我所看到的topX项目的排序列表。随着流的继续,我根据需要放大列表。然后,如果需要,我使用bisect_left查找插入点 以下是我到目前为止的算法: from bisect import bisect_left from random import

我正在寻找一种单遍算法,用于查找流中浮点数的topX百分比,在这种情况下,我事先不知道总浮点数。。。但其浮动量大约为500-3000万。它需要单次传递,因为数据是动态生成的,并且第二次重新创建准确的流

到目前为止,我所使用的算法是保存一个到目前为止我所看到的topX项目的排序列表。随着流的继续,我根据需要放大列表。然后,如果需要,我使用
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]
在实际情况中,数据不是来自任何标准分布(否则我可以使用一些统计知识)


如果您有任何建议,我们将不胜感激。

我不确定是否有任何方法可以可靠地做到这一点,因为当您看到更多元素时,“顶部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,但是在看到足够多的零之后,你最终不得不选择前十个元素中的所有元素。同样的模式可以扩展到任何足够大的流——最终总是可能被外观所误导,并丢弃实际上应该保留的元素。因此,除非您提前知道流的确切长度,否则我认为这是不可能的(除非在到达流的末尾之前将每个元素都保存在内存中)。

您必须将整个流存储在内存中

证明:您有一个数字序列,n1,…,nk。k的值未知。你怎么知道什么时候可以忘记你?当您看到x*k/100数字大于ni时。然而,因为k是未知的,所以您永远不能这样做


因此,唯一的“一次通过”算法必须将整个序列存储在内存中。

下面是使用cProfile运行该算法的输出。您的代码似乎运行良好,因为大多数调用都是0.000(percall)。它看起来很慢,只是因为你有很多东西要处理。如果您想优化,您必须尝试减少项目,因为这就是所谓的999999,这似乎是不必要的

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
    1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
    7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
    1    0.001    0.001    0.001    0.001 bisect.py:1(<module>)
    1    0.001    0.001    0.001    0.001 hashlib.py:55(<module>)
    6    0.000    0.000    0.000    0.000 hashlib.py:91(__get_openssl_constructor)
    1    0.000    0.000    0.000    0.000 os.py:743(urandom)
    1    0.000    0.000    0.000    0.000 random.py:100(seed)
1000000    0.731    0.000    0.876    0.000 random.py:355(uniform)
    1    0.003    0.003    0.004    0.004 random.py:40(<module>)
    1    0.000    0.000    0.000    0.000 random.py:647(WichmannHill)
    1    0.000    0.000    0.000    0.000 random.py:72(Random)
    1    0.000    0.000    0.000    0.000 random.py:797(SystemRandom)
    1    0.000    0.000    0.000    0.000 random.py:91(__init__)
    1    2.498    2.498   13.313   13.313 test.py:12(get_top_X_percent)
    1    0.006    0.006   16.330   16.330 test.py:3(<module>)
1000001    0.545    0.000    1.422    0.000 test.py:8(data_gen)
1000000    1.744    0.000    1.744    0.000 {_bisect.bisect_left}
    1    0.000    0.000    0.000    0.000 {_hashlib.openssl_md5}
    1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha1}
    1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha224}
    1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha256}
    1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha384}
    1    0.000    0.000    0.000    0.000 {_hashlib.openssl_sha512}
    1    0.000    0.000    0.000    0.000 {binascii.hexlify}
    1    0.000    0.000    0.000    0.000 {function seed at 0x100684a28}
    6    0.000    0.000    0.000    0.000 {getattr}
    6    0.000    0.000    0.000    0.000 {globals}
1000004    0.125    0.000    0.125    0.000 {len}
    1    0.000    0.000    0.000    0.000 {math.exp}
    2    0.000    0.000    0.000    0.000 {math.log}
    1    0.000    0.000    0.000    0.000 {math.sqrt}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
1009989    0.469    0.000    0.469    0.000 {method 'insert' of 'list' objects}
 999999    8.477    0.000    8.477    0.000 {method 'pop' of 'list' objects}
1000000    0.146    0.000    0.146    0.000 {method 'random' of '_random.Random' objects}
    1    0.000    0.000    0.000    0.000 {posix.close}
    1    0.000    0.000    0.000    0.000 {posix.open}
    1    0.000    0.000    0.000    0.000 {posix.read}
    2    1.585    0.792    3.006    1.503 {sorted}

正如其他答案所讨论的那样,除了将整个流存储在内存中,您真的做不到更好的事情。考虑这样做,特别是因为5-30万浮动可能只有40-240MB的内存是可管理的。 假设您存储了整个流,从算法上讲,获取topX百分比的最快方法是首先使用线性时间选择算法查找截止元素(topX百分比中的最小元素):

然后,再次通过流,过滤掉所有小于截止元件的元件


此方法是线性时间和线性空间,这是您所能期望的最佳方法。

此程序有效吗?问题是什么?要查看您的代码,请尝试“是”,它工作得很好(尽管速度很慢)。。。我想知道是否有人有更狡猾的方法。。。我不知道代码审查。。。有什么方法可以迁移它吗?对于工作代码来说,很好的选择是尝试这个网站对于不工作的代码来说是mor。如果在迭代过程中的某个时候,你存储了前100个项目,然后出现了一个比第100个项目小但不多的项目。然后,稍后您将展开以存储101个项目,但是您丢弃的上一个项目现在比您现在看到的所有项目都大。。。换句话说,您丢弃了真正的第101项。有道理吗?@Lasse:有道理。。。也看不出有什么办法。嗯,他必须创建一个最坏情况下的数据结构。如果他知道不可能超过3000万件物品,他就必须抓住前X件物品,其中X是相对于3000万件物品计算的。然后,在他知道实际计数后丢弃不需要的值。这是个好主意。。。我可以对上界进行最坏情况的猜测,然后在后记中将其缩小。所有
pop
调用都来自这样一个事实,即在这种情况下,当数据已经排序时,我有最坏情况。正如@Lasse评论的那样:除非知道k的上界,否则必须存储整个流。
python -m cProfile test.py