2011-07-01 47 views
16

我們如何做一個std :: vector的後面插入分析(push_back)?它的分期時間是O(1)每次插入。特別是在video in channel9 by Stephan T Lavavejin this (17:42 onwards)中,他表示爲了獲得最佳性能,微軟採用這種方法將向量的容量提高了1.5左右。std :: vector插入的分期分析

這個常數是如何確定的?

+2

您確定自己的意思是*插入*?我認爲只有*末尾的插入*,或'push_back',是分期償還的O(1);任意插入在需要移動的元素數量上是線性的。 –

+0

哦,我對此感到懷疑,提及它......將其編輯 – jemmanuel

+8

爲什麼地球上有人投票結束「非主題」和「非建設性」?投票結束爲「重複」可能是可以理解的,但不是理由。潛在選民:當你不明白問題時,請不要投票。 –

回答

14

假設你的意思是push_back而不是插入,我相信重要的部分是乘以某個常量(而不是每次抓取更多的元素),只要你這樣做,你會得到攤銷的恆定時間。更改因素會改變平均情況和最壞情況的表現。

具體而言: 如果您的常數因子太大,您將具有良好的平均案例性能,但是壞的最差情況下性能尤其是在陣列變大時。例如,想象僅僅因爲您推送了第10001個元素而加倍(2x)10000大小的矢量。編輯:正如Michael Burr間接指出的那樣,這裏的真正成本可能是你的記憶力會比你想要的要大得多。我會補充說,如果你的因素太大,緩存問題會影響速度。只要說如果你的增長遠大於你的需求,就會有真正的成本(內存和計算)。然而,如果你的常數因子太小,比如說(1.1x),那麼你將會得到很好的最差情況表現,但糟糕的平均表現,因爲你將不得不承擔重新分配太多的成本倍。

Also, see Jon Skeet's answer to a similar question previously.(感謝@Bo佩爾鬆)

,稍微介紹一下分析:假設你有n項目你是推回去的M倍增因子。然後,重新分配的次數將大致以nlog_M(n))的基數M爲基數。並且i重新分配將花費與M^iMi次方成比例)成比例。那麼所有回傳的總時間將爲M^1 + M^2 + ... M^(log_M(n))。回推的數量是n,因此你得到這個系列(這是一個幾何系列,在極限內大致減少到(nM)/(M-1))除以n。這大致是一個常數,M/(M-1)

對於M的較大值,您將超出很多並且分配的數量遠遠超過您經常需要的數量(我在上面提到過)。對於小數值M(接近1),該常數M/(M-1)變大。這個因素直接影響平均時間。

+0

爲什麼使用10000個元素的向量加倍分配比分配一個新的塊還要多一些元素(大於10000)? –

+0

是的意思是在後面的惰性......已編輯的問題.. – jemmanuel

+0

所以你說的是,過大的因素真正的問題是,你只會佔用太多的內存?或者我錯過了這一點?你是對的,真正的成本可能是重新分配後發生的複製。 –

1

呃,這個分析非常簡單,當你熟悉數字系統時,比如我們通常的十進制。然後,爲了簡單起見,假設每次達到當前容量時,分配一個新的10x作爲大緩衝區。

如果原始緩衝區大小爲1,則第一次重新分配副本1元素,第二次(現在緩衝區大小爲10)複製10個元素,依此類推。因此,有五次重新分配,例如,您執行1 + 10 + 100 + 1000 + 10000 = 11111個元素副本。乘以9,得到99999;現在加1,你有100000 = 10^5。或者換句話說,這樣做後,爲支持這5次重新分配而執行的元素副本的數量是(10^5-1)/ 9。

5次重新分配之後的緩衝區大小乘以5,乘以10,即爲10^5。這大約比元素複製操作的數量大9倍。這意味着複製的時間在所得到的緩衝區大小上大致是線性的。

使用基數2而不是10可得(2^5-1)/ 1 = 2^5-1。

等等用於其他基地(或增加緩衝區大小的因素)。

乾杯& hth。

7

你可以做數學來試圖找出這種事情是如何工作的。

一種流行的漸近分析方法是Bankers方法。你所做的就是用額外的成本來標記你所有的業務,「節省」它以便以後支付昂貴的操作費用。


讓我們做一些假設轉儲到簡化的數學:

  • 寫入到陣列的成本1。 (用於在陣列之間插入和移動)
  • 分配較大的陣列是免費的。

而且我們的算法是這樣的:

function insert(x){ 
    if n_elements >= maximum array size: 
     move all elements to a new array that 
     is K times larger than the current size 
    add x to array 
    n_elements += 1 

很顯然,當我們要移動元素到新數組的「最壞情況」發生。讓我們嘗試通過在插入成本中添加一個固定標記d來分攤這一成本,使其每個操作的總計爲(1 + d)

剛剛調整了一個數組的大小後,我們將其中的(1/K)填滿,並且沒有保存任何錢。 當我們填充陣列時,我們可以確保至少保存了d * (1 - 1/K) * N。由於這筆錢必須能夠支付所有N個元素被移動,我們可以計算出Kd之間的關係:

d*(1 - 1/K)*N = N 
d*(K-1)/K = 1 
d = K/(K-1) 

一個有用的表格:

k d  1+d(total insertion cost) 
1.0 inf inf 
1.1 11.0 12.0 
1.5 3.0 4.0 
2.0 2.0 3.0 
3.0 1.5 2.5 
4.0 1.3 2.3 
inf 1.0 2.0 

從這個

所以你可以得到一個粗略的數學家關於時間/記憶折衷如何解決這個問題的想法。當然有一些注意事項:當元素數量減少時,我沒有重新縮小數組,這隻涵蓋了最糟糕的情況,即沒有元素被刪除,分配額外內存的時間成本沒有考慮。

他們很可能跑了一堆實驗測試,最終弄清楚了這些,儘管我寫的大部分內容都是不相關的。

相關問題