我写了compiler cache for MSVC(很像ccache的gcc)。我要做的一件事就是删除缓存目录中最旧的目标文件,将缓存修剪为用户定义的大小。
现在,我基本上有一个元组列表,每个元组都是最后的访问时间和文件大小:
# 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个最旧的项目,其累积大小超过某个值。
答案 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
条目。