【问题标题】:What is the resizing factor of lists in PythonPython中列表的大小调整因素是什么
【发布时间】:2019-02-11 05:11:01
【问题描述】:

例如,Java 中的ArrayLists 的大小调整因子为 2。当ArrayList 所环绕的数组空间不足时,该数组的所有元素都将转移到一个新数组,即 2乘以原始数组的大小。

由于 Python 列表/数组自然是动态的,它们的大小调整因素是什么?还是他们使用其他缩放方法?如果是这样,那是什么方法?它的渐近运行时(大 O)是多少?

【问题讨论】:

标签: python python-3.x list data-structures asymptotic-complexity


【解决方案1】:

对于 CPython,特别是在调整大小期间的过度分配在 objects/listobject.c 中实现。它被描述为

温和,但......足以在性能不佳的系统 realloc() 存在的情况下,在很长的 appends() 序列中提供线性时间摊销行为。

当列表需要增长时,它会增长到所需大小的大约 112.5%。特别是实际大小为new_size + new_size >> 3 + (newsize < 9 ? 3 : 6)

【讨论】:

  • 注意:这严格来说是当前版本的 CPython(参考解释器)的实现细节。过度分配规则随着时间的推移而改变,将来可能会继续改变,并且可能在所有其他非 CPython 解释器上有所不同(例如,Jython 可能会在后台合理地使用 ArrayList 并继承其行为)。没有记录的语言功能需要任何特定的实现。依赖特定的大小调整算法是个坏主意。
  • 不过,这个答案对于概念理解和复杂性分析非常有用,如果没有别的。
  • @ShadowRanger 在他们最喜欢的当前安装的 Python 版本中,哪里可以找到这种行为的文档?我怎么才能确切地知道我拥有哪个版本?
  • @Gulzar:你没有。它在源代码本身中提到,也许是设置它的错误跟踪器条目。这对语言本身来说并不重要,只是一个实现细节,并且记录它会束缚他们的手,而不会提供任何有用的好处。 CPython 是参考解释器,因此除非您特意安装其他解释器,否则它就是您所拥有的。
【解决方案2】:

回答您的其他问题:在ArrayList 或类似的末尾添加一个项目需要 O(1) 时间,只要有 any 因素k > 1 这样重新分配总是会生成一个数组,该数组是前一个数组的 k 倍。

实际使用的因子并不重要,只要它大于 1。替代的重新分配方案可能具有不同的复杂性。例如,如果重新分配增加了一个常数,那么添加一个项目需要 O(N) 时间。

这就是为什么所有专业编写的动态增长数组每次都会增长一些因素。该因子提供了浪费空间比例的固定上限 ((k-1)/k),与每个项目可以复制的平均次数的固定上限进行权衡添加序列。

假设您正在使用因子 k 并且您刚刚执行了 Nth 重新分配。这意味着您添加了大约 k^N 个项目。你复制了多少项目?设 C。那么:

C = k^(N-1) + k(N-2) + ... + 1

kC - C = k^N + k^(N-1) - k^(N-1) + k^(N-2) - k^(N-2) ... - 1

kC - C = k^N - 1

C = (k^N-1) / (k-1)

每项添加的副本数,则为C/K^N,差不多是1/(k-1) >.

所以 Java 的 k=2 因子意味着每个添加操作大约有一个副本,最多有 50% 个未使用的插槽,而 CPython 的因子 k =1.125 表示每次添加操作大约有 8 个副本,最多有 11% 个未使用的插槽。

【讨论】:

    猜你喜欢
    • 2013-08-13
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    • 2020-02-21
    • 1970-01-01
    • 2015-10-01
    • 2017-12-28
    • 2017-01-13
    相关资源
    最近更新 更多