【问题标题】:Swift - performance wise - comparing two arrays and get the difference in each and common in bothSwift - 性能方面 - 比较两个数组并获得每个数组的差异和两者的共同点
【发布时间】:2017-12-13 04:24:11
【问题描述】:

希望你今天过得愉快。

我正在尝试了解执行以下操作的最快方法是什么:

假设我有这两个Arrays:

var firstArray = ["a","b","c"]
var secondArray = ["a","d","e"]

我想得到一个输出:

1)Array 内部 firstArray 但没有 secondArray 的对象。
1)Array 内部 secondArray 但没有 firstArray 的对象。
3)Array 属于firstArraysecondArray 之间的common 对象。

所以基本上输出是:

1) ["b","c"]
2) ["d","e"]
3)["a"]

这里的主要问题是了解这样做的最有效方法是什么。非常感谢!

【问题讨论】:

  • 数组是否排序?它们是否包含字符串,或者这只是一个示例?如果它们不是字符串,可以将对象与< 进行比较吗?
  • 将数组转换为集合,然后使用subtractingintersection
  • 如果元素的顺序对您来说并不重要,并且如果(如您的示例中)所有元素都是唯一的,您可能需要考虑使用集合代数而不是有序集合类型,例如作为Array。例如。在 Swift 中使用 Set 允许您将 subtract(_:) 用于 1) 和 2),将 intersection(_:) 用于 3),它们都使用 O(1)(摊销)查找来比较集合之间的元素(与例如使用 Array 的 O(n) contains(_:) (带有 Equatable 元素)来查找某些指定元素的存在)。见Set

标签: ios arrays swift performance sorting


【解决方案1】:

如果您的数组已排序并且每个数组中的项目都是唯一的,那么最快的方法是只处理每个项目一次。首先比较每个数组中的第一项;如果它们相等,则将其放入公共数组中,然后转到第二个项目。如果一个项目小于另一个,它将进入较小项目的唯一数组,然后您继续移动到较小数组中的下一个项目。继续此过程,直到您用完一个数组的项,然后将第二个数组的剩余项放入该数组的唯一项数组中。

var i = 0
var j = 0

let a = ["a", "b", "c"]
let b = ["a", "d", "e"]

var aUnique = [String]()
var bUnique = [String]()
var common = [String]()

while i < a.count && j < b.count {
    if a[i] == b[j] {
        common.append(a[i])
        i += 1
        j += 1
    } else if a[i] < b[j] {
        aUnique.append(a[i])
        i += 1
    } else {
        bUnique.append(b[j])
        j += 1
    }
}

if i < a.count {
    // put remaining items into aUnique
    aUnique += a[i ..< a.count]
} else if j < b.count {
    // put remaining items into bUnique
    bUnique += b[j ..< b.count]
}

print(common)  // ["a"]
print(aUnique) // ["b", "c"]
print(bUnique) // ["d", "e"]

分析

  • 此算法每次通过循环将一项附加到数组之一。如果两个数组相对于彼此是唯一的,或者只有它们的最后一项是共同的,它将最多循环a.count + b.count - 1 次。
  • 如果两个数组相同,它将仅循环a.count 次。
  • 如果数组b 的所有元素都大于数组a 的元素,它将只循环a.count 次。如果数组a的所有元素都大于数组b的元素,则只会循环b.count次。

【讨论】:

  • 嘿!谢谢你的详细解答!我已经为你的两个答案做了一个基准(我的第一个)。在这个特定场景中,我最终将面临的场景中,我们可以假设每个数组的值都是唯一的 + 我们不需要特定的顺序。我正在为 xCode 基准测试项目 dropbox.com/s/5t5ktu2soea56wu/Benchmark.zip?dl=0 添加一个链接。我已经在真实设备(6+)上对其进行了测试,似乎@vacawama 方法更快。但是为了了解我想确定的知识。如果基准测试正确执行,您能否快速查看并更新?再次感谢您!
  • 我已经更新了方法。我已经在 6+ 上测试了 100k 个元素。再次感谢您的详细解答!非常感激。总是在学习新的东西 :) 这就是结果: vacawamaMethod 所用的时间: 0.000427007675170898 秒。 ========= ========= =================== ========= dfriMethod 所用时间: 1.23837202787399 秒。 @vacawama 方法要快得多。
【解决方案2】:

我假设你的数组元素是Equatable

如果它们也是Hashable,并且元素的顺序对您来说并不重要,并且如果(如您的示例中)所有元素都是唯一的,您可能需要考虑使用集合代数而不是有序集合类型,例如Array。例如。在 Swift 中使用 Set 允许您对 1) 和 2) 使用 subtract(_:)subtracting(_:) mutating/non-methods,对于 3) 使用 intersection(_:)/formIntersection(_:),它们都使用 O(1) (摊销)查找用于比较集合之间的元素(与例如使用 O(n) contains(_:) of Array(带有Equatable元素)来查找某些指定元素的存在相比)。

更多详细信息,请参阅language reference for Set 以及 vadian 链接到的线程:


如果每个数组中的元素不是唯一的,并且您希望保留多个元素以及元素之间的顺序,则可以使用其中一个数组的 Set 表示,同时过滤另一个数组。

例如,对于:

var firstArray = ["a","b","c"]
var secondArray = ["a","d","e"]

A) 在O(n):

let excludeElements = Set(secondArray)        // O(n)
secondArray = secondArray
    .filter { !excludeElements.contains($0) } // O(n) due to O(1) (amortized) .contains lookup

B) 在O(n):

let excludeElements = Set(firstArray)         // O(n)
secondArray = secondArray
    .filter { !excludeElements.contains($0) } // O(n) due to O(1) (amortized) .contains lookup

C) 在O(n) 中,使用在firstArray 中出现的顺序和重复:

let includeElements = Set(secondArray)  // O(n)
let commonElements = firstArray
    .filter(includeElements.contains)   // O(n) due to O(1) (amortized) .contains lookup

C) 在O(n) 中,使用在secondArray 中出现的顺序和重复:

let includeElements = Set(firstArray) // O(n)
let commonElements = secondArray
    .filter(includeElements.contains) // O(n) due to O(1) (amortized) .contains lookup

性能?

上面只考虑了渐近时间复杂度,并没有考虑任何实际的基准测试。通常,诸如 filter 之类的函数方法比 forwhile 循环要慢,因此 如果 性能成为您的应用程序的问题,此时您应该考虑执行分析以及自定义基准测试算法中可能存在的瓶颈。

此外,如果已知您的数组已排序,则有更有效的方法可以遍历它们并过滤掉结果。参见例如以下线程(C语言,但逻辑是重要部分):

【讨论】:

  • 在我回答之前,我没有查看您的答案或您的链接。我如何选择名称“a”、“b”、“i”、“j”和“common”就像您的链接答案一样,这有点令人毛骨悚然,但这只是一个巧合(事实上这些都是“显而易见的”名称用于命名受损程序员)。
  • @vacawama 我想,伟大的思想都一样:)我也认为明确的 Swift 版本很好,而且您的回答还包括 OP 查询的独特案例。
  • 嘿!谢谢你的详细解答!我已经为你的两个答案做了一个基准(我的第一个)。在这个特定的场景中,我最终将面临的场景中,我们可以假设每个数组的值都是唯一的 + 我们不需要特定的顺序。我正在为 xCode 基准测试项目 dropbox.com/s/5t5ktu2soea56wu/Benchmark.zip?dl=0 添加一个链接。我已经在真实设备(6+)上对其进行了测试,似乎@vacawama 方法更快。但是为了了解我想确保的知识。如果基准测试正确执行,您能否快速查看并更新?再次感谢您!
  • @RoiMulia,我的算法有错字,我注意到你的版本仍然不正确。 b 的索引始终是 j,因此正确的行是 } else if a[i] &lt; b[j] {。您应该在发布模式下测试性能。
  • 我已经更新了方法。我已经在 6+ 上测试了 100k 个元素。再次感谢您的详细解答!非常感激。总是在学习新东西 :) 这就是结果: vacawamaMethod 所用的时间: 0.000427007675170898 秒。 ========= ========= =================== ========= dfriMethod 所用时间: 1.23837202787399 秒。 @vacawama 方法更快
猜你喜欢
  • 2013-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-06
  • 1970-01-01
  • 2014-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多