jem*_*uel 17 c++ algorithm stl stdvector amortized-analysis
我们如何分析std :: vector中后面的插入(push_back)?它的摊销时间是每次插入O(1).特别是在史蒂芬牛逼Lavavej在Channel9的视频,并在此(17:42以后),他说,以获得最佳性能微软的这个方法的实现由大约1.5增加了向量的能力.
这个常数如何确定?
Chr*_* A. 16
假设你的意思push_back
,而不是插入,我认为重要的部分是由多重一些不变(而不是抓住每一次N多元素),只要你这样做你会得到分期常量时间.更改因子会改变平均情况和最差情况.
具体来说:如果你的常数因子过大,你将获得良好的平均大小写性能,但是最糟糕的情况下表现不佳,尤其是当阵列变大时.例如,想象一下,加倍(2x)10000大小的向量只是因为你推送了第10001个元素.编辑:正如迈克尔伯尔间接指出的那样,这里真正的成本可能是你的记忆力比你需要的要大得多.我想补充一点,如果你的因素太大,就会有影响速度的缓存问题.我只想说,如果你的成长比你需要的大得多,那就有实际成本(记忆和计算).
但是,如果你常数因子太小,说(1.1倍),那么你将有良好的最坏情况下的性能,但糟糕的表现平平,因为你将不得不承担重新分配的次数太多的成本.
此外,请参阅Jon Skeet先前对类似问题的回答. (谢谢@Bo Persson)
关于分析的更多信息:假设你有n
推回的物品和倍增系数M
.然后,重新分配的数量将大致M
为n
(log_M(n)
)的对数基数.而i
重新分配的成本与M^i
(M
对于i
权力)成正比.那么所有回击的总时间将是M^1 + M^2 + ... M^(log_M(n))
.推回的数量是n
,因此你得到这个系列(几何系列,并大致减少到(nM)/(M-1)
极限)除以n
.这大致是一个常数M/(M-1)
.
对于较大的数值,M
你会超过很多,并且分配比你经常需要的更多(我在上面提到过).对于小值M
(接近1),该常数M/(M-1)
变大.这个因素直接影响平均时间.
你可以做数学试图弄清楚这种事情是如何起作用的.
使用渐近分析的一种流行方法是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
Run Code Online (Sandbox Code Playgroud)
显然,当我们必须将元素移动到新数组时,会发生"最坏情况".让我们尝试通过d
为插入成本添加一个常量标记来分摊这个,使其达到(1 + d)
每个操作的总和.
在调整阵列大小之后,我们已经填充了(1/K)并且没有省钱.当我们填满阵列时,我们肯定至少可以d * (1 - 1/K) * N
保存起来.由于这笔钱必须能够支付所有被移动的N个元素,我们可以找出K
和之间的关系d
:
d*(1 - 1/K)*N = N
d*(K-1)/K = 1
d = K/(K-1)
Run Code Online (Sandbox Code Playgroud)
一张有用的表:
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
Run Code Online (Sandbox Code Playgroud)
因此,你可以得到一个粗略的数学家关于时间/内存权衡如何适用于这个问题的想法.当然有一些注意事项:当元素数量减少时,我没有过度缩小数组,这只能解决最糟糕的情况,即没有删除任何元素,并且没有考虑分配额外内存的时间成本.
他们最有可能进行了大量的实验测试,最终弄清楚这一点,尽管我写的大部分内容都无关紧要.
归档时间: |
|
查看次数: |
6349 次 |
最近记录: |