当 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)
来完成,与乘法相比,这在处理器上非常便宜。
希望对您有所帮助!
我是摊销分析的新手。我注意到动态数组的常见做法是在 运行 超出 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)
来完成,与乘法相比,这在处理器上非常便宜。
希望对您有所帮助!