要通過測試添加一些額外的驗證到Ami的答案我做了一個簡單的循環緩衝區從一個numpy陣列只使用直接索引來插入。基本上每個插入只是改變隊列中最舊元素的值。
代碼不是完全沒有bug,但它可以作爲一些簡單的性能基準測試的基礎。
import math
import numpy as np
class CircFifo():
"""
helper class, uses a numpy array to provide a circular fixed size fifo
interface.
put(element): removes the oldest element and
places a new one
get(): returns the oldest entry
empty(): returns true if fifo is empty
full(): returns true if fifo is full
"""
def __init__(self, size):
self.array = np.empty(shape=(size, 2))
self.size = size
self.array[:] = np.NAN
self.top = 0
self.bottom = 0
def put(self, row):
self.array[self.top, :] = row
self.top += 1
if self.top == self.size:
self.top = 0
def get(self):
if not math.isnan(self.array[self.bottom, 0]):
row = copy.deepcopy(self.array[self.bottom, :])
self.array[self.bottom, :] = float('NaN')
self.bottom += 1
if self.bottom == self.size:
self.bottom = 0
if math.isnan(self.array[self.bottom, 0]):
self.bottom = 0
self.top = 0
return row
def empty(self):
if math.isnan(self.array[self.bottom, 0]):
return True
else:
return False
def full(self):
if self.size - np.count_nonzero(
np.isnan(self.array[:, 0])) == self.size:
return True
else:
return False
在帖子中的答案的正確性似乎通過我運行一個簡單的測試來確認。我測試了對deque對象的插入性能。即使是1000個插入deque,它也是一個動態而非靜態的數據結構(與我的靜態循環fifo相反),顯然勝過了循環fifo。
下面是簡單的測試,我跑
In [5]: import time
In [6]: circFifo = CircFifo(300)
In [7]: elapsedTime = 0
In [8]: for i in range(1, 1000):
...: start = time.time()
...: circFifo.put(np.array([[52, 12]]))
...: elapsedTime += time.time() - start
...:
In [9]: elapsedTime
Out[9]: 0.010616540908813477
In [21]: queue = deque()
In [22]: elapsedTime = 0
In [23]: for i in range(1, 1000):
....: start = time.time()
....: queue.append(np.array([[52, 12]]))
....: elapsedTime += time.time() - start
....:
In [24]: elapsedTime
Out[24]: 0.00482630729675293
我知道,這個基準是不是言之有物,但它變得相當明顯,雙端隊列爲快得多。至少需要這樣的插入量。
我認爲如果這個循環fifo是在C中用一個靜態數組實現的,它不可能輕易超越。由於基本上C的靜態數組是最簡單的並且具有較少的開銷數據結構。
「直接索引必須非常高效」 - 這取決於您的意思是「高效」。在大O方面,你顯然不能比* O(1)*做得更好,但是這忽略了常數因子的大小。正如@AmiTavory正確指出的那樣,索引需要Python函數調用,與那些低級語言相比,它更昂貴。 –