Swift - 高阶函数的速度和效率(降低)

时间:2016-07-29 09:36:20

标签: arrays swift algorithm performance

快速询问有关大输入数据的高阶快速函数的效率。在最近的测试中,我有一个关于找到equlibirum索引的问题。在数组中 - 即数组的索引,其中索引之下的所有元素的总和等于索引之上的所有元素的总和

  

该阵列的平衡指数是任何整数P,使得0≤P<1。   N和较低指数的元素之和等于   更高指数的要素,即

          A[0] + A[1] + ... + A[P−1] = A[P+1] + ... + A[N−2] + A[N−1].

挑战在于编写一个简短的函数来计算第一个(或任何)被认为是“平衡”的指数。 我把一个简单的片段放在一起,得分很高,但却失败了一些表演&#39;使用大量输入数据的测试(数组大小约为100,000)。

这是代码

public func solution(inout A : [Int]) -> Int {
   var index = 0;
        for _ in A {
            let sumBefore = A[0...index].reduce(0) { $0 + $1 }
            let sumAfter = A[index...A.count-1].reduce(0) { $0 + $1 }
            if (sumBefore == sumAfter) { return index; }
            index += 1;
        }
        return -1;
}

是否有人能够解释为什么代码在大型数据集或任何推荐的替代方案中表现如此糟糕?

这里,例如是失败性能测试的描述:

  

大型性能测试,O(n ^ 2)解决方案应该失败。

     

✘超时错误   运行时间:&gt; 6.00秒,时间限制:0.22秒

4 个答案:

答案 0 :(得分:4)

问题不在于#34;内置功能的表现非常差。&#34; 您的解决方案很慢,因为在每次迭代中,N个元素都是 添加(N是数组的长度)。它会更有效率 计算总和一次并在总和之前更新&#34;&#34; 和&#34;总和后#34;穿过阵列时。这减少了 从O(N^2)O(N)的复杂性:

public func solution(A : [Int]) -> Int {

    var sumBefore = 0
    var sumAfter = A.reduce(0, combine: +)

    for (idx, elem) in A.enumerate() {
        sumAfter -= elem
        if sumBefore == sumAfter {
            return idx
        }
        sumBefore += elem
    }
    return -1
}

(Swift 2.2,Xcode 7.3.1)

说明:

  • 没有理由将数组作为inout参数传递。
  • 运算符(在本例中为+)可以作为参数传递给reduce()函数。
  • enumerate()返回一系列数组索引 相应的元素,这节省了另一个数组访问。

还要注意更多&#34; Swifty&#34;设计将是返回类型 如果找不到解决方案,则Int?nil

答案 1 :(得分:4)

看起来挑战失败了,因为您的解决方案是O(n^2)

您的for循环以及内部的2个连续reduce会使您的解决方案变为〜O(2*n^2),因为reduce会再次遍历所有元素。

更简单的解决方案是首先计算整个和,然后迭代元素一次,逐个减去整个和中的每个值,从而可以访问左右和,以进行比较。

使用Swift 3.0,Xcode 8:

func findEquilibriumIndex(in array: [Int]) -> Int? {
  var leftSum = 0
  var rightSum = array.reduce(0, combine: +)


  for (index, value) in array.enumerated() {
    rightSum -= value

    if leftSum == rightSum {
        return index
    }

    leftSum += value
  }

  return nil
}

let sampleArray = [-7, 1, 5, 2, -4, 3, 0]

findEquilibriumIndex(in: sampleArray)

答案 2 :(得分:0)

incrementalSums扩展名

如果您定义此扩展程序

extension Array where Element : SignedInteger {
    var incrementalSums: [Element] {
        return Array(reduce([0]) { $0.0 + [$0.0.last! + $0.1] }.dropLast())
    }
}

给定一个Int(s)数组,您可以构建一个数组,其中Int位置的n-th代表从0(n-1)的值的总和在原始数组中。

实施例

[1, 2, 3, 10, 2].incrementalSums // [0, 1, 3, 6, 16]

equilibriumIndex函数

现在你可以构建一个像这样的函数

func equilibriumIndex(nums: [Int]) -> Int? {
    let leftSums = nums.incrementalSums
    let rightSums = nums.reversed().incrementalSums.reversed()
    return Array(zip(leftSums, rightSums)).index { $0 == $1 }
}

答案 3 :(得分:0)

以下是Swift 3中解决方案的功能版本

  let total = sampleArray.reduce(0,+) 
  var sum    = 0
  let index  = sampleArray.index{ v in defer {sum += v}; return sum * 2 == total - v } 

如果我理解正确,结果索引中的元素被排除在每一方的总和之外(我不确定其他解决方案是否实现)