【问题标题】:Whats faster: Insert at index 0 or array.Reverse?什么更快:在索引 0 或 array.Reverse 处插入?
【发布时间】:2017-02-22 15:22:53
【问题描述】:

我从我的数据库中获取数据的顺序与我需要的相反。为了正确排序,我有几个选择:我可以插入在数组的索引 0 处获得的每条新数据,或者只是附加它然后在最后反转数组。像这样的:

let data = ["data1", "data2", "data3", "data4", "data5", "data6"]
var reversedArray = [String]()

for var item in data {
   reversedArray.insert(item, 0)
}

// OR

reversedArray = data.reverse()

以下哪个选项会更快?随着物品数量的增加,两者之间会有什么显着差异吗?

【问题讨论】:

  • 试一试并测量!
  • 您的意思是reversedArray.insert(item, at: 0)reversedArray = data.reversed(),对吗?你做过基准测试吗?如果有,你发现了什么?
  • 没有使用 swift 的经验,所以我只是猜测 .reverse 应该更有效。 IMO 你也应该考虑 ReverseRandomAccessCollection stackoverflow.com/questions/34558390/…
  • 好吧,我想我也可以以相反的顺序访问我的数组元素,这可能是最快的:array[array.count - 1 - i]

标签: swift reverse


【解决方案1】:

添加新元素的平均复杂度约为 O(1)。根据文档,反转数组也有constant complexity

Insertion 有一个complexity O(n),其中 n 是数组的长度,您将一个一个地插入所有元素。

所以追加然后反转应该更快。但是,如果您只处理几十个元素,您将不会看到明显的差异。

【讨论】:

  • 获得反向视图具有恒定的复杂性。反转数组(使用array.reverse()newArray = array.reversed())具有 O(n) 复杂度。
  • @MartinR 这不是 array.reversed() 所做的吗?从文档中:“reversed():返回以相反顺序呈现集合元素的视图。[...] 复杂性:O(1)”
  • 有多种“逆向”方法。在array = array.reversed()这个方法developer.apple.com/reference/swift/sequence/1641355-reversed的SequenceType被调用。
【解决方案2】:

通过在开头重复插入项目来创建数组将是最慢的,因为它所花费的时间与所涉及的项目数量的平方成正比。

(澄清:我的意思是反向构建整个数组所需的时间与 n^2 成正比,因为每次插入所需的时间与当前数组中的项目数成正比,因此将是 1 + 2 + 3 + 。 .. + n 与 n 的平方成正比)

在构建后反转数组会快得多,因为它所花费的时间与所涉及的项目数量成正比。

仅以相反的顺序访问项目会更快,因为您避免了反转数组。

查看“大 O 表示法”以获取更多信息。另请注意,对于较小的 n 值,运行时间为 O(n^2) 的算法可以胜过运行时间为 O(n) 的算法。

【讨论】:

  • 好吧,但是我看到的所有常用数组实现都没有设置为允许在不移动现有对象的情况下在开始插入,因此每次插入可能需要与元素数量成正比的时间,并且 1 + 2 + 3 + ... + n 与 n 的平方成正比。
  • 每个插入都是可以的,但是他通过一次插入一个来构建一个包含 n 个元素的列表,使用所有插入构建整个列表是 O(n^2) 因为插入成本是 1 + 2 + 3 + ... + n 这是 O(n^2)
  • 抱歉造成混淆,我编辑了我的答案以便更清楚。但是我认为你对日志 n 是错误的。考虑获取序列 1 + 2 + 3 + ... + n 的两个副本并以相反的顺序对它们进行配对以获得: (n + 1) + (n - 1 + 2) + ... + (1 + n) .这等于原始总和的 2 倍,显然等于 n*(n+1)。所以原始总和等于 n^2 + n 的一半,即 O(n^2)。
【解决方案3】:

我的测试结果……

    do {
        let start = Date()
        (1..<100).forEach { _ in
            for var item in data {
                reversedArray.insert(item, at: 0)
            }
        }
        print("First: \(Date().timeIntervalSince1970 - start.timeIntervalSince1970)")
    }



    do {
        let start = Date()
        (1..<100).forEach { _ in
            reversedArray = data.reversed()
        }
        print("Second: \(Date().timeIntervalSince1970 - start.timeIntervalSince1970)")
    }


First: 0.0124959945678711
Second: 0.00890707969665527

有趣的是,运行它们 10,000 次......

First: 7.67399883270264
Second: 0.0903480052947998

【讨论】:

  • 因此,在末尾反转数组大约需要在 0 处插入 70%。当时大多数人的假设都是正确的。有趣,感谢您的测试。
  • 这不是能做到的吗?很抱歉批评 - 但如果“大多数人的假设是正确的”,你为什么要发布这个问题?你可以测试一下!
  • 我在这里问是因为我认为有人已经有了答案
  • 虽然这种测试可以描述一个特定的案例,但它一般来说对性能的影响相对较少。通过以小容量增量扩展阵列,您会触发一些用于指数容量扩展的糟糕情况 (developer.apple.com/reference/swift/array/1538388-capacity)。使用 reserveCapacity 预分配容量可能会显着改变结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-22
相关资源
最近更新 更多