如何对Python列表进行部分排序?

时间:2010-12-29 16:24:14

标签: python sorting

我写了compiler cache for MSVC(很像ccachegcc)。我要做的一件事就是删除缓存目录中最旧的目标文件,将缓存修剪为用户定义的大小。

现在,我基本上有一个元组列表,每个元组都是最后的访问时间和文件大小:

# First tuple element is the access time, second tuple element is file size
items = [ (1, 42341),
          (3, 22),
          (0, 3234),
          (2, 42342),
          (4, 123) ]

现在我想在此列表中执行部分排序,以便对前N个元素进行排序(其中N是元素的数量,以便它们的大小总和超过45000)。结果基本上应该是这样的:

# Partially sorted list; only first two elements are sorted because the sum of
# their second field is larger than 45000.
items = [ (0, 3234),
          (1, 42341),
          (3, 22),
          (2, 42342),
          (4, 123) ]

我并不关心未分类条目的顺序,我只需要列表中N个最旧的项目,其累积大小超过某个值。

3 个答案:

答案 0 :(得分:16)

您可以使用heapq模块。在列表中调用heapify(),然后调用heappop(),直到满足您的条件。 heapify()是线性的,heappop()是对数的,所以它可能会尽可能快。

heapq.heapify(items)
size = 0
while items and size < 45000:
  item = heapq.heappop(items)
  size += item[1]
  print item

输出:

(0, 3234)
(1, 42341)

答案 1 :(得分:2)

我不知道罐装的任何东西,但是您可以使用任何类型的变体来执行此操作,该变量将排序列表从一端逐步构建到另一端,但是当已经对足够的元素进行排序时它会停止。 Quicksort将是显而易见的选择。选择排序会做,但这是一个可怕的类型。正如马可建议的那样,Heapsort也会这样做,将整个阵列的堆积作为沉没成本。不能以这种方式使用Mergesort。

要特别关注quicksort,您只需要跟踪到目前为止已经对数组进行了多远处理的高水位标记,以及这些元素的总文件大小。在每个子排序的末尾,您可以通过添加新排序的元素来更新这些数字。当它通过目标时放弃排序。

您可能还会发现通过更改分区选择步骤可以提高性能。如果您只希望对数组的一小部分进行排序,您可能更喜欢不平衡的分区元素。

答案 2 :(得分:-1)

部分排序(参见the Wikipedia page)比实际排序更有效。算法类似于排序算法。我将概述基于堆的部分排序(虽然它不是该页面上最有效的)。

你想要最老的。您将元素逐个粘贴在堆中,并在堆中的最新元素变得太大时弹出它。由于堆保持较小,因此您不需要为插入和删除元素付出太多代价。

在标准情况下,您需要最小/最大k元素。您需要满足总条件的最旧元素,因此通过保留total_size变量来跟踪总条件。

代码:

import heapq

def partial_bounded_sort(lst, n):
    """
    Returns minimal collection of oldest elements
     s.t. total size >= n.
    """
    # `pqueue` holds (-atime, fsize) pairs.
    # We negate atime, because heapq implements a min-heap,
    #  and we want to throw out newer things.
    pqueue = []
    total_size = 0

    for atime, fsize in lst:
        # Add it to the queue.
        heapq.heappush(pqueue, (-atime, fsize))
        total_size += fsize

        # Pop off newest items which aren't needed for maintaining size.
        topsize = pqueue[0][1]
        while total_size - topsize >= n:
            heapq.heappop(pqueue)
            total_size -= topsize
            topsize = pqueue[0][1]

    # Un-negate atime and do a final sort.
    oldest = sorted((-priority, fsize) for priority, fsize in pqueue)

    return oldest

您可以采取一些措施来微优化此代码。例如,您可以使用前几个项填写列表,并立即将其全部堆积。

复杂性可能优于排序。在您的特定问题中,您不知道要返回的元素数量,甚至不知道一次可以在队列中有多少元素。在最坏的情况下,您几乎排序所有列表。您可以通过预处理列表来查看是否更容易找到新事物或旧事物集。

如果你想跟踪哪些项目被删除,你可以在原始列表中保留两个“指针”:一个用于跟踪你处理的内容,另一个用于标记“空闲”空间。处理项目时,从列表中删除它,当从堆中丢弃一个项目时,将其放回列表中。该列表最终将包含不在堆中的项目,以及最后的一些None条目。