【问题标题】:Swift calculate incremental average with reduce and使用 reduce 和 Swift 计算增量平均值
【发布时间】:2016-02-23 20:41:01
【问题描述】:

建议是逐步计算平均值 下面的代码是我发现计算增量平均值的最佳方法,以便将其用于大数字和/或大数组

下面是给这个数组加倍的例子

let values = [14.0,12.0, 23.4,37.5,11.46]

var index = 1

let avg = values.reduce(0.0) { return $0 + ( $1 - $0 ) / Double(index++) }

平均为 19.672。并且有效。

从你的角度来看是正确的吗?

有没有办法通过以下方式完成此操作:

let avg = values.averageIncr()

我不喜欢的是我必须使用和索引?

[更新]

更进一步,感谢@Martin R 的贡献

protocol NumericType:Equatable {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
    init(_ value : Int)
}

extension Double : NumericType { }

extension Array where Element : NumericType {
    mutating func avg_inc_adding( element: Element, var startAvg:Element? = Element(0)  ) throws -> Element{
        if count > 0 && startAvg! == Element(0) {
            startAvg = self.avg_inc()
        }
        append(element)
        return startAvg! + (element - startAvg!) / Element(self.count - 1)
    }
    func avg_inc() -> Element {
        return enumerate().reduce(Element(0)) { $0 + ( $1.1 - $0 ) / Element($1.0 + 1) }
    }
}

通过这种方式,我可以执行以下操作:

var average:Double = values.avg_inc()
do {
    average = try values.avg_inc_adding(34.6,startAvg: z)
}catch {

}

符合我的需要,我希望也能满足其他人的需要。

【问题讨论】:

  • 谢谢,我会用这种方法提出这个逻辑。应该更好
  • @Leo:这不是stackoverflow.com/questions/28288148/… 的副本。这不是简单的平均计算,OP的问题是迭代步骤涉及到当前元素索引。我看不出在引用的线程中是如何解决这个问题的。

标签: swift average


【解决方案1】:

无需“外部变量”即可获得相同的结果 与

let avg = values.enumerate().reduce(0.0) { $0 + ( $1.1 - $0 ) / Double($1.0 + 1) }

因为enumerate() 返回一个索引/元素对序列。

将其作为扩展方法实现有点复杂,但 可能:

protocol NumericType {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
    init(_ value : Int)
}

extension Double : NumericType { }

extension Array where Element : NumericType {
    func averageIncr() -> Element {
        return enumerate().reduce(Element(0)) { $0 + ( $1.1 - $0 ) / Element($1.0 + 1) }
    }
}

let avg = values.averageIncr()

Array 扩展中的 Element 类型只能限制为 协议,而不是类型。与例如类似What protocol should be adopted by a Type for a generic function to take any number type as an argument in Swift?How can we create a generic Array Extension that sums Number types in Swift?,你必须定义一个 协议,其中包含计算所需的所有方法。

限制Element: FloatingPointType 是不够的,因为 FloatingPointType 协议没有定义任何算术运算(+、-、/)。


Swift 3 更新:从 Swift 3/Xcode 8 开始,浮点 类型符合FloatPoint 协议并定义了 算术运算(+、-、/)。因此自定义协议是 不再需要:

extension Array where Element: FloatingPoint {
    func averageIncr() -> Element {
        return enumerated().reduce(Element(0)) { $0 + ( $1.1 - $0 ) / Element($1.0 + 1) }
    }
}

【讨论】:

  • 关键还在于 enumerate() 将在数组上添加更多循环(我正在研究 enumerate() 现在是如何工作的),如果数组有大量元素
  • @tylyo:您的原始代码和我建议的扩展方法(使用枚举)在我的计算机上在大约 0.008 秒内计算一个包含 1,000,000 个随机数的数组的增量平均值。性能没有显着差异。
  • Element(0) 意味着是通用的,所以如果它是 Int 它会做 Int(0)...如果 Double 它会 Double(0) 并初始化为它的类型?
  • @Honey: Element(0) 调用init(_ value : Int) 方法,这是NumericType 协议所必需的。添加浮点类型有这样一个init(_ value : Int) 方法。 – 但是 Swift 3 不再需要自定义协议,我已经相应地更新了答案。
  • @Honey: enumerated() 返回一个由(index, element) 元组组成的序列。
猜你喜欢
  • 2014-02-04
  • 2015-03-25
  • 1970-01-01
  • 2014-07-02
  • 2021-09-06
  • 1970-01-01
  • 1970-01-01
  • 2020-03-01
  • 1970-01-01
相关资源
最近更新 更多