我正在尝试编写一个脚本,该脚本在执行时会附加新的可用信息并删除超过10分钟的数据。
我想知道最有效的方法,性能方面,跟踪每个信息元素的具体时间,同时还删除10分钟以上的数据。
我的新手想法是将带有时间戳的信息 - [信息,时间] - 附加到双端队列,并在一段时间内继续评估双端队列的结束以删除超过10分钟的任何内容......我怀疑这是最好的方式。
有人能提供一个例子吗?感谢。
答案 0 :(得分:3)
执行此操作的一种方法是使用按时间戳键入的排序树结构。然后你可以找到第一个元素> = 10分钟前,并在此之前删除所有内容。
使用bintrees
库作为示例(因为它的键切片语法使得读取和写入非常容易......):
q = bintrees.FastRBTree.Tree()
now = datetime.datetime.now()
q[now] = 'a'
q[now - datetime.timedelta(seconds=5)] = 'b'
q[now - datetime.timedelta(seconds=10)] = 'c'
q[now - datetime.timedelta(seconds=15)] = 'd'
now = datetime.datetime.now()
del q[:now - datetime.timedelta(seconds=10)]
这将删除所有内容,但不包括now-10s,这应该是c
和d
。
这样,找到要删除的第一个元素需要log N时间,并且删除下面的N个元素应该是平均情况下的摊销日志N但是最坏情况N.所以,你的整体最坏情况时间复杂度没有改善,但是你的平均情况确实如此。
当然,管理树而不是双端队列的开销非常高,如果你处理一个非常小的队列,那么很容易高于N / log N步骤的节省。
还有其他对数数据结构映射更合适,如pqueue / heapqueue(由stdlib中的heapq
实现)或时钟环;我刚刚选择了一棵红黑树,因为(使用PyPI模块)它是最容易演示的。
答案 1 :(得分:1)
如果你只是追加到最后,并且值总是按照排序的顺序排列,那么你根本不需要像树或堆那样的对数数据结构;您可以在list
或collections.deque
之类的任何已排序的随机访问结构中进行对数搜索。
问题是删除list
或deque
中任意点的所有内容需要O(N)时间。它没有理由 ;你应该能够以摊销的常数时间(使用del q[:pos]
或q.popleft(pos)
)将N个元素从deque中删除,只是collections.deque
不会这样做。如果您找到或编写 具有该功能的双端队列,您可以写下:
q = deque()
now = datetime.datetime.now()
q.append((now, 'a'))
q.append((now - datetime.timedelta(seconds=5), 'b')
q.append((now - datetime.timedelta(seconds=10), 'c')
q.append((now - datetime.timedelta(seconds=15), 'd')
now = datetime.datetime.now()
pos = bisect.bisect_left(q, now - datetime.timedelta(seconds=10))
del q[:pos]
我不确定PyPI上是否存在这样的deque
,但the C source to collections.deque可用于fork,Python source from PyPy,或者你可以包装C或C ++ {{ 1}}输入,或者从头开始写一个......
或者,如果您期望双端队列中的“当前”值始终是总长度的一小部分,那么您可以在O(M)时间内通过不破坏性地使用deque来实现:
deque
事实上,在这种情况下,您也可以使用q = q[pos:]
;它在右边有O(1)附加,并且从列表中删除最后M个项目的开销很小,这是一种复制M项目的方法,就像你要找到的一样。
答案 2 :(得分:0)
又一个答案,有更多限制:
如果您可以使用例如一分钟的精确度,那么您只需要10个列表。
与我的其他受限制的答案不同,这并不要求你只是附加在右边;你可以在中间追加(虽然你会在同一分钟后追上任何其他值)。
缺点是你实际上不能删除超过10分钟的所有东西;你只能删除第10个桶中的所有东西,最多可以关闭1分钟。您可以通过选择如何舍入来选择这意味着什么:
当然,您可以使用较小的水桶,例如100个6秒间隔的水桶,而不是10分钟的1分钟间隔,以便尽可能地减少误差。推动太多远,你会破坏效率;一个600毫秒的1ms间隔的列表几乎和1M条目列表一样慢。*但是如果你需要1秒甚至50ms,那可能就好了。
这是一个简单的例子:
def prune_queue(self):
now = time.time() // 60
age = now - self.last_bucket_time
if age:
self.buckets = self.buckets[-(10-age):] + [[] for _ in range(age)]
self.last_bucket_time = now
def enqueue(self, thing):
self.prune_queue()
self.buckets[-1].append(thing)
*当然你可以将它与对数数据结构结合起来 - 一个600000桶的红黑树很好。