【问题标题】:Scala - Efficient element wise sum of two arraysScala - 两个数组的有效元素之和
【发布时间】:2021-01-16 03:36:33
【问题描述】:

我有两个数组,我想将它们缩减为一个数组,其中每个索引处都有原始数组中两个元素的总和。例如:

val arr1: Array[Int] = Array(1, 1, 3, 3, 5)
val arr1: Array[Int] = Array(2, 1, 2, 2, 1)
val arr3: Array[Int] = sum(arr1, arr2) 

// This should result in:
// arr3 = Array(3, 2, 5, 5, 6)

我看过这篇文章:Element-wise sum of arrays in Scala,我目前使用这种方法(zip/map)。但是,将其用于大数据应用程序时,我担心它的性能。使用这种方法,必须至少遍历数组两次。在效率方面有更好的方法吗?

【问题讨论】:

  • 如果您有大数据,您是否考虑过使用流式解决方案?例如 akka 流,或其他解决方案..?

标签: arrays scala


【解决方案1】:

最有效的方法可能是懒惰地做。

与任何面向集合的东西一样,Scala 2.12 和 2.13 将有所不同(此代码是 Scala 2.13,但 2.12 将相似......可能会扩展 IndexedSeqLike,但我不确定)

import scala.collection.IndexedSeq
import scala.math.Numeric

case class SumIndexedSeq[+T: Numeric](seq1: IndexedSeq[T], seq2: IndexedSeq[T]) extends IndexedSeq[T] {
  override val length: Int = seq1.length.min(seq2.length)
  override def apply(i: Int) =
    if (i >= length) throw new IndexOutOfBoundsException
    else seq1(i) + seq2(i)
 }

Arrays 可以隐式转换为collection.IndexedSeq 的子类型。这将计算每次访问时对应元素的总和(这通常是可取的,因为可以使用可变的IndexedSeq)。

如果您需要Array,只需一次遍历即可获得一个

val arr3: Array[Int] = SumIndexedSeq(arr1, arr2).toArray

但是SumIndexedSeq 可以在任何可以使用Seq 的地方使用而无需遍历。

作为进一步的优化,特别是如果您确定底层集合/数组不会发生变异,您可以添加一个缓存,这样您就不会将相同的元素添加到一起两次。如果您愿意,它也可以推广到T 上的任何二进制操作(在这种情况下,可以删除Numeric 约束)。

正如 Luis 所说,对于性能问题:实验和基准测试。值得记住的是,缓存实现很可能需要将每个元素装箱以放入缓存中,因此您可能需要多次访问相同的元素才能使缓存获胜(并且足够大的缓存可能具有对分布式系统稳定性的影响)。

【讨论】:

    【解决方案2】:

    嗯,首先,与所有与性能相关的事情一样,唯一的答案就是进行基准测试。

    其次,你确定你需要简单的可变、不变、奇怪的数组吗?你不能使用 VectorArraySeq 之类的东西吗?

    第三,您可以这样做或使用while 循环,这将是相同的。

    val result = ArraySeq.tabulate(math.min(arr1.length, arr2.length)) { i =>
      arr1(i) + arr2(i)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-12
      • 1970-01-01
      • 2020-08-13
      • 2017-03-01
      • 1970-01-01
      相关资源
      最近更新 更多