ruby数组中shift / unshift的运行时间是多少?

时间:2011-12-02 07:26:40

标签: ruby big-o

有人知道红宝石阵列中的移位和非移位效率如何?

从数组的开头删除并且必须移动内存中的每个元素会变得非常低效。我认为ruby会以其他方式做到这一点。

以下任何信息都会有所帮助:
- 算法运行时
- 实施
- 一般效率
- 移位/取消移位是否可以接受用于队列(在C ++中不会这样)

谢谢!

4 个答案:

答案 0 :(得分:7)

在早期版本的Ruby中(在~2012之前),unshift是一个O(n)操作。但是,在this commitreleased in Ruby 2.0.0中添加了一项优化,使unshift 摊销了O(1),这意味着它平均可以保证O(1),但是单独的操作可以是O(n)。这与shift的运行时间相同。

This CS Stack Exchange post很好地解释了它是如何工作的,以及你如何最终得到O(1)摊销的运行时间(它关于C ++' vector::push_back ,但它的工作方式相同)。

答案 1 :(得分:3)

您可以查看here并查看unshift方法的C源代码(只需点击描述块)。非常清楚:如果我们没有足够的内存容量,增加内存容量,向前移动数组的当前内容,将传递的参数复制到内存块开头的空闲空间。因此O(n)unshift

答案 2 :(得分:2)

我发现回答这个问题的最简单和最明确的方法就是对它进行基准测试。

require 'benchmark'

Benchmark.bm do |x|
    iterations = 10000000
    x.report("push") {
        a = []
        iterations.times do a.push(10) end
    }
    x.report("unshift") {
        a = []
        iterations.times do a.unshift(10) end
    }
    a = []
    iterations.times do a.push(10) end
    x.report("shift") {
        iterations.times do a.shift() end
    }
    a = []
    iterations.times do a.push(10) end
    x.report("pop") {
        iterations.times do a.pop() end
    }
end

在运行ruby版本2.0.0的系统上,这将返回结果:

             user     system      total        real
push     0.880000   0.030000   0.910000 (  0.917213)
unshift  0.920000   0.090000   1.010000 (  1.026208)
shift    0.780000   0.030000   0.810000 (  0.810293)
pop      0.710000   0.000000   0.710000 (  0.724865)

似乎pushpopshiftunshift的时间大致相同。

使用iterations的不同值再次运行此代码,可以得到与我更改的数量iterations成比例的结果。这意味着无论iterations的值如何,每个操作的平均时间总是相同,这意味着每个操作的运行时间与数组的长度无关,因此运行时间为O(1)

我想说这可以作为队列使用。

答案 3 :(得分:0)

根据this article,它似乎根本没有移动,只是递增一个指针并返回它。所以在效率方面它的效率非常高(O(1))。然而,该文章提到了潜在的内存泄漏,可能会或可能不会出现在最近的版本中。