当 space 中的 运行 时,为什么动态数组的大小特别加倍?

Why do dynamic arrays specifically double in size when running out of space?

我是摊销分析的新手。我注意到动态数组的常见做法是在 运行 超出 space 时将其大小加倍。我们选择将尺寸加倍是否有特定原因?为什么不是三倍或四倍?使用摊销分析选择加倍是否有具体解释?还是选择随意?

您可能已经发现 in this post, the amortized complexity of the dynamic array is O(1). If you see the analysis, you will find that there is not any difference in the asymptotic time complexity if you change 2 to 3 or 4 or even to any other constant (greater than 1) number, even decimals. For example, in Microsoft Visual C++, using 1.5 as a growth factor [1](在提供的 link 中查看更多案例)。

因此,2 不是这里的特定增长因素,其他因素也在使用。此外,如前所述 here:

Many textbooks, however, use a = 2 for simplicity and analysis purposes.

通过按任何常数因子缩放来增加数组大小足以使运行时间达到 O(n)。要看到这一点,请注意,如果最终数组大小以 n 结尾,并且我们在每一步中按系数 m 缩放,那么增长数组的总工作量将为

1 + m + m2 + ... + m1+logm n.

要了解这是为什么,请注意(如果数组从大小 1 开始)然后数组将以大小 1、m、m2、...、直到它达到大小 n。最后一个增长步骤发生在 mk = n 时,这发生在 k = logm n 时。将一个增长步骤考虑在内以超过 n 占这里的 +1。

以上求和是几何级数求和,求和为

(m2 + logmn - 1) / (m - 1)

= (m2n - 1)/ (m - 1)

≤ n · (m2 / (m - 1))

所以基本上任何大于 1 的指数都有效,但主要系数取决于我们选择的 m 的选择。对于大的 m,这个系数大约等于 m,我们最终浪费了很多精力和 space 增长数组。如果m越接近1,分母就会越来越大,越受关注

选择 m = 2 给出的前导系数为 4,这是相当低的。选择 1.5 给出了 4.5 的领先系数。那更高,但不是很多。然而,选择 1.5 还有其他一些优势:

  • 分配的数组,如果我们继续增长数组,绝不会比我们之前的数组大 50% 以上。与翻倍相比,这减少了数据结构的开销。
  • 如果我们需要增大数组,先前数组的大小之和超过了新数组的大小(检查这个 - 2 的幂不这样做)。这使得内存分配器更有可能从旧的废弃数组中回收 space 以适应新数组。
  • 乘以 1.5 可以通过计算 size + (size >> 1) 来完成,与乘法相比,这在处理器上非常便宜。

希望对您有所帮助!