Swift数组中最大值及其索引的有效算法

时间:2017-02-09 05:31:13

标签: arrays swift max

我写了一个算法是Swift,用于在Swift数组中查找最大值及其索引。这是受Matlab& amp;中“max.m”功能的启发。八度。

这里的专家能否建议一种在速度方面改进算法的方法?我的意思是它可以更快地制作,或者你认为这对于大型阵列(有时是15000个样本)来说是一种合理的方法。

public func max (y: [Double]) -> (Int, Double) {

let inLen = y.count

var out = Double()
var outp = Int()

if (1 == inLen) { // if only one element
    out = y[0]
    outp = 0
} else if (0 == inLen) { // if no elements
    out = -1
    outp = -1
} else {
    out = y[0]
    outp = 0
    for ii in 1...inLen-1 {
        if (out<y[ii]){
            out = y[ii]
            outp = ii
        }
    }
}
return (outp, out)
}

// Call the function

let y: [Double] = [3, 4, 5, 6, 7, 8, 9, 100, 100, 11, 12, 13, 14, 15, -8, -7, -7, 99]
let (ind, value) = max(y: y)
print(ind) // 7
print(value) // 100.0 

2 个答案:

答案 0 :(得分:4)

原始代码的代码审核

以下是我要做的改变。这一切都很粗糙(如果所有这些都编译的话,我还没有检查过),但会让你走上正轨。

  1. 使代码通用

    public func max (y: [Double]) -> (Int, Double) {
    

    变为:

    public func max<T: Comparable>(y: [T]) -> (Int, T) {
    
  2. 删除无用的参数关键字y,并将y重命名为有意义的内容

    public func max<T: Comparable>(_ array: [T]) -> (Int, T) {
    
  3. 为结果添加关键字:

    public func max<T: Comparable>(_ array: [T]) -> (index: Int, value: T) {
    
  4. 将其添加为ArrayRandomAccessCollection的扩展程序,完全取代对参数的需求:

    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element) {
            // ...
        }
    }
    
  5. 内联不必要的变量inLen,或至少更好地命名,例如count

  6. outoutp重命名为更好的内容,例如maxValuemaxIndex

  7. 不要在Swift中使用yoda比较。 =并不是Swift中的一个表达,所以没有风险在if语句中意外地导致赋值而不是比较。它会触发编译器错误。另外,省略

    if (1 == count) {
    

    应该是

    if (count == 1) {
    
  8. 在if语句中省略了无用的parathesis:

    if count == 1 {
    
  9. 以更合理的顺序重新排列计数检查。而不是1,0,1 +,将其命令为0,1,1 +。

  10. 尽早返回,而不是使用if / elseif / else来跳过代码块来获得共同的回报。而不是:

    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element) {
            var maxValue = Double()
            var maxIndex = Int()
    
            if count == 0 { // if no elements
                maxValue = -1
                maxIndex = -1
            } else if count == 1 { // if only one element
                maxValue = self[0]
                maxIndex = 0
            } else {
                maxValue = self[0]
                maxIndex = 0
                for i in 1...inLen-1 {
                    if (maxValue < self[i]){
                        maxValue = self[i]
                        maxIndex = i
                    }
                }
            }
            return (index: maxIndex, value: maxValue)
        }
    }
    

    试试这个:

    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element)? {
            var maxValue = Double()
            var maxIndex = Int()
    
            if count == 0 { return (index: -1, value: -1) } 
            if count == 1 { return (index: 0, value: self[0]) }
    
            maxValue = self[0]
            maxIndex = 0
            for i in 1...count-1 {
                if (maxValue < self[i]) {
                    maxValue = self[i]
                    maxIndex = i
                }
            }
            return (index: maxIndex, value: maxValue)
        }
    }
    
  11. 现在您可以删除maxValuemaxIndex

    的前向声明
    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element) {
            if count == 0 { return (index: -1, value: -1) } 
            if count == 1 { return (index: 0, value: self[0]) }
    
            var maxValue = self[0]
            var maxIndex = 0
            for i in 1...count-1 {
                if (maxValue < self[i]) {
                    maxValue = self[i]
                    maxIndex = i
                }
            }
            return (index: maxIndex, value: maxValue)
        }
    }
    
  12. 避免重写1 ... x-1之类的内容,使用..<:     for in in 1 ..&lt;算{

  13. 在这种情况下,使用self.indices更好,它实现了同样的目标:

    for i in self.indicies {
    
  14. 如果您需要两个索引以及与这些索引相关的值,请使用enumerated()

    for (index, value) in self.enumerated() {
    
  15. 切勿在Swift中使用-1""等标记值。我们有选择权表达缺乏价值。使用它们:

    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element)? {
            if count == 0 { return nil } 
            if count == 1 { return (index: 0, value: self[0]) }
    
            var maxValue = self[0]
            var maxIndex = 0
            for (index, value) in self.enumerated() {
                if (maxValue < value) {
                    maxValue = value
                    maxIndex = index
                }
            }
            return (index: maxIndex, value: maxValue)
        }
    }
    
  16. 我还会使用元组赋值来缩短一点:

    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element)? {
            if count == 0 { return nil } 
            if count == 1 { return (index: 0, value: self[0]) }
    
            var (maxIndex, maxValue) = (0, self[0])
            for (index, value) in self.enumerated() {
                if (maxValue < value) {
                    (maxIndex, maxValue) = (index, value)
                }
            }
            return (index: maxIndex, value: maxValue)
        }
    }
    
  17. 现在我们正在使用元组赋值,我们可以看到我们可以将maxValue和maxIndex组合成一个元组,我们直接返回:

    extension Array where Element: Comparable {
        public func max() -> (index: Int, value: Element)? {
            if count == 0 { return nil } 
            if count == 1 { return (index: 0, value: self[0]) }
    
            var maxElement = (index: 0, value: self[0])
            for (index, value) in self.enumerated() {
                if (maxElement.value < value) { maxElement = (index, value) }
            }
            return maxElement
        }
    }
    
  18. 以下是如何调用新方法:

    let array: [Double] = [3, 4, 5, 6, 7, 8, 9, 100, 100, 11, 12, 13, 14, 15, -8, -7, -7, 99]
    
    if let (maxIndex, maxValue) = array.max() {
        print("The max element is \(maxValue) at index \(maxIndex)")
    }
    else {
        print("The array is empty, and has no max element or index.")
    }
    

    即时的Swift方法

    let array: [Double] = [3, 4, 5, 6, 7, 8, 9, 100, 100, 11, 12, 13, 14, 15, -8, -7, -7, 99]
    
    if let (maxIndex, maxValue) = array.enumerated.max{ $0.element < $1.element } {
        print("The max element is \(maxValue) at index \(maxIndex)")
    }
    else {
        print("The array is empty, and has no max element or index.")
    }
    

    MartinR的方法,改进了

    这是MartinR的方法的一个包装,以便更容易与其他Swift代码集成:

    func max(of array: [Double]) -> (index: Int, value: Double)? {
        var maxValue = Double()
        var maxIndex = vDSP_Length()
        vDSP_maxviD(array, 1, &maxValue, &maxIndex, vDSP_Length(array.count))
    
        if maxValue == -Double.infinity { return nil }
    
        return (index: Int(maxIndex), value: maxValue)
    }
    

答案 1 :(得分:3)

您可以使用vDSP_maxviD)() 来自Accelerate框架的功能。 vDSP功能使用 vDSP_Length (又名UInt)用于数组计数和索引,所以你必须转换 Swift互操作性的Int索引。

import Accelerate

let array: [Double] = ...

var elem = 0.0
var vdspIndex: vDSP_Length = 0
vDSP_maxviD(array, 1, &elem, &vdspIndex, vDSP_Length(array.count))
let idx = Int(vdspIndex)

print("max:", elem, "at index:", idx)

事实证明这比你明确要快5倍 循环15,000个元素数组(在发布模式下编译的iMac上)。