在python中获取大小为N的未排序列表中获取k个最小数字的最快方法?

时间:2015-11-10 04:59:12

标签: python algorithm time-complexity

使用python在大小为N的未排序列表中获取k个最小数字的最快方法是什么? 对大数字列表进行排序,然后获得k个最小数字,或者通过在列表中找到最小值k次来获得k个最小数字,确保从搜索中删除找到的最小值是否更快在下次搜索之前?

6 个答案:

答案 0 :(得分:9)

您可以使用堆队列;它可以在O(NlogK)时间内从大小为N的列表中提供K个最大或最小的数字。

Python标准库包含heapq module,已完成heapq.nsmallest() function已完成实施:

import heapq

k_smallest = heapq.nsmallest(k, input_list)

在内部,这会创建一个大小为K的堆,其中包含输入列表的前K个元素,然后迭代剩余的N-K个元素,将每个元素推送到堆中,然后弹出最大的元素。这样的推送和弹出需要记录K时间,使整个操作为O(NlogK)。

该函数还优化了以下边缘情况:

  • 如果K为1,则使用min()函数代替,给出O(N)结果。
  • 如果K> = N,则该函数使用排序,因为在这种情况下O(NlogN)将击败O(NlogK)。

更好的选择是使用introselect algorithm,它提供O(n)选项。我所知道的唯一实现是使用numpy.partition() function

import numpy

# assuming you have a python list, you need to convert to a numpy array first
array = numpy.array(input_list)
# partition, slice back to the k smallest elements, convert back to a Python list
k_smallest = numpy.partition(array, k)[:k].tolist()

除了要求安装numpy之外,这还需要N个内存(与heapq的K相比),因为为该分区创建了列表的副本。

如果你只想要索引,你可以使用,对于任何一种变体:

heapq.nsmallest(k, range(len(input_list)), key=input_list.__getitem__)  # O(NlogK)
numpy.argpartition(numpy.array(input_list), k)[:k].tolist()  # O(N)

答案 1 :(得分:3)

编辑:这假设列表是不可变的。如果列表是一个数组并且可以修改,则可以使用线性方法。

使用大小为O(n * log k)的堆,您可以将复杂性降低到k + 1

  1. 最初将第一个k元素放入最小堆中。
  2. 对于每个后续元素,将元素添加为叶子并堆积。
  3. 将最后一个元素替换为下一个元素。
  4. Heapify可以在对数时间内完成,因此时间复杂度如上所述。

答案 2 :(得分:2)

您可以使用selection algorithmO(kn)中执行此操作。一旦kn >= n log n,切换到排序。也就是说,选择算法上的常量往往比快速排序上的常量高很多,因此真的需要比较i (kn)j (n log n)。在实践中,除非您处理大型 n 或非常小的 k ,否则通常更有必要进行排序。

编辑:见评论。它实际上是 lot 更好。

答案 3 :(得分:2)

您可能需要查看heapq

In [109]: L = [random.randint(1,1000) for _ in range(100)]

In [110]: heapq.nsmallest(10, L)
Out[110]: [1, 17, 17, 19, 24, 37, 37, 45, 63, 73]

答案 4 :(得分:2)

如果第k个最小数字的列表不需要排序,可以使用像introselect这样的选择算法在O(n)时间内完成。标准库没有一个,但NumPy有numpy.partition的工作:

fork()

答案 5 :(得分:0)

在heapq中使用nsmallest数字的代码较少,但如果您希望自己实现它,这是一种简单的方法。这个解决方案只需要循环数据一次,但是由于他应用并且在O(log n)上运行,因此该算法在较小数量的k上表现最佳。

import heapq

def getsmallest(arr, k):
    m = [-x for x in l[:k]]
    heapq.heapify(m)
    for num in arr[5:]:
        print num, m
        heapq.heappush(m, max(-num, heapq.heappop(m)))
    return m

if __name__ == '__main__':
    l = [1,2,3,52,2,3,1]
    print getsmallest(l, 5)
相关问题