小编典典

在Python中获取列表的小n个元素

algorithm

我需要在Python中获得列表中较小的n个数字。我需要做到这一点非常快,因为它是性能的关键部分,需要重复很多次。

n通常不大于10,并且列表通常包含约20000个元素。每次调用函数时,列表总是不同的。无法进行分类。

最初,我编写了此功能:

def mins(items, n):
    mins = [float('inf')]*n
    for item in items:
        for i, min in enumerate(mins):
            if item < min:
                mins.insert(i, item)
                mins.pop()
                break
    return mins

但是此函数无法击败对整个列表进行排序的简单sorted(items)[:n]。这是我的测试:

from random import randint, random
import time

test_data = [randint(10, 50) + random() for i in range(20000)]

init = time.time()
mins = mins(test_data, 8)
print 'mins(items, n):', time.time() - init

init = time.time()
mins = sorted(test_data)[:8]
print 'sorted(items)[:n]:', time.time() - init

结果:

mins(items, n): 0.0632939338684
sorted(items)[:n]: 0.0231449604034

sorted()[:n]快三倍。我相信这是因为:

  1. insert()操作的成本很高,因为Python列表不是链接列表。
  2. sorted()是优化的c函数,而我的是纯python。

有什么办法可以击败sorted()[:n]吗?我应该使用C扩展名,Pyrex或Psyco还是类似的名称?

预先感谢您的回答。


阅读 334

收藏
2020-07-28

共1个答案

小编典典

您实际上需要排序的分钟序列。

mins = items[:n]
mins.sort()
for i in items[n:]:
    if i < mins[-1]: 
        mins.append(i)
        mins.sort()
        mins= mins[:n]

这运行 更快,因为除非证明它的值大于给定项目,否则您甚至都不会查看分钟。大约是原始算法时间的1/10。

这在我的Dell上运行时间为零。我必须运行10次才能获得可测量的运行时间。

mins(items, n): 0.297000169754
sorted(items)[:n]: 0.109999895096
mins2(items)[:n]: 0.0309998989105

使用bisect.insort而不是附加和排序可以进一步加快头发的速度。

2020-07-28