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