簡體   English   中英

直接索引一個 numpy 數組的時間復雜度是多少

[英]What's the time complexity of indexing a numpy array directly

我假設當有一個 numpy 數組時,讓我們說

>>>>nArray
array([[  23425.     ,  521331.40625],
       [  23465.     ,  521246.03125],
       [  23505.     ,  528602.8125 ],
       [  23545.     ,  531934.75   ],
       [  23585.     ,  534916.375  ],
       [  23865.     ,  527971.1875 ]])

直接索引必須非常有效。

我想像nArray[0, 1] = 69696420必須使用哈希表,它的時間復雜度接近 O(1)。 那正確嗎?

更新

正如兩個答案所指出的,索引 numpy 數組不涉及散列。 兩個答案都清楚地解釋了索引是如何發生的。

更新 2

我添加了一個簡單的基准測試來證明答案的有效性

一方面

必須使用一個哈希表,它的時間復雜度接近 O(1)。 那正確嗎?

不完全正確。 Numpy array基本上是連續的同構內存塊,在維度等方面有一些額外的信息。 因此,訪問是O(1) ,並且只涉及一些簡單的數學運算來確定內存中的位置。

另一方面

索引必須非常有效。

不幸的是,這根本不是真的。 除了邊界檢查(數組所做的)之外,涉及純 python 的一切都非常低效(並且訪問涉及純 python 調用)。 Numpy 數組訪問也不例外 您應該盡可能嘗試使用向量運算。

不涉及哈希表。 Numpy數組就是數組,顧名思義,地址是這樣計算的:

address of nArray[x, y] = base address + A * x + B * y

為了通過測試對 Ami 的答案添加一些額外的驗證,我從一個僅使用直接索引進行插入的 numpy 數組創建了一個簡單的循環緩沖區。 基本上每次插入只會改變隊列中最舊元素的值。

該代碼並非完全沒有錯誤,但它可以作為一些簡單的性能基准測試的基礎。

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 次插入雙端隊列,它也用作動態而非靜態數據結構(與我的靜態循環先入先出相反),也明顯優於循環先入先出。

這是我運行的簡單測試

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

我知道這個基准測試的信息量並不大,但很明顯 deque 速度要快得多。 對於至少插入的數量。

我希望如果在 C 中使用靜態數組實現圓形 fifo,它就不會輕易被超越。 因為基本上 C 的靜態數組是最簡單的,可用的數據結構開銷較少。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM