首页 > 解决方案 > 对两个列表进行元素总和的最快方法

问题描述

我可以使用Zipped函数进行元素运算,如求和。让我有两个列表L1L2,如下所示

val L1 = List(1,2,3,4)
val L2 = List(5,6,7,8)

我可以通过以下方式对元素进行求和

(L1,L2).zipped.map(_+_)

结果是

List(6, 8, 10, 12) 

正如预期的那样。

我在我的实际代码中使用了Zipped函数,但这需要太多时间。实际上,我的列表大小超过1000 个,并且我有超过1000 个列表,我的算法是迭代的,其中迭代可能高达10 亿

在代码中我必须做以下事情

list =( (L1,L2).zipped.map(_+_).map (_  * math.random) , L3).zipped.map(_+_)

L1L2L3的大小相同。此外,我必须在集群上执行我的实际代码。

在Scala中获取列表元素总和的最快方法是什么?

标签: scalalistperformanceapache-sparkelementwise-operations

解决方案


一种选择是使用Streaming实现,利用惰性可能会提高性能。

使用LazyList的示例 (在 Scala 中引入2.13

def usingLazyList(l1: LazyList[Double], l2: LazyList[Double], l3: LazyList[Double]): LazyList[Double] =
  ((l1 zip l2) zip l3).map {
    case ((a, b), c) =>
      ((a + b) * math.random()) + c
  }

还有一个使用fs2.Stream的示例 (由fs2库引入)

import fs2.Stream
import cats.effect.IO

def usingFs2Stream(s1: Stream[IO, Double], s2: Stream[IO, Double], s3: Stream[IO, Double]): Stream[IO, Double] =
  s1.zipWith(s2) {
    case (a, b) =>
      (a + b) * math.random()
  }.zipWith(s3) {
    case (acc, c) =>
      acc + c
  }

但是,如果这些仍然太慢,最好的选择是使用普通数组。

这是一个使用ArraySeq的示例 (在 Scala 中2.13也引入了),它至少会保持不变性。如果您愿意,可以使用原始数组,但要小心。
(如果你愿意,你也可以使用collections-parallel module来提高性能)

import scala.collection.immutable.ArraySeq
import scala.collection.parallel.CollectionConverters._

def usingArraySeq(a1: ArraySeq[Double], a2: ArraySeq[Double], a3: ArraySeq[Double]): ArraySeq[Double] = {
  val length = a1.length

  val arr = Array.ofDim[Double](length)

  (0 until length).par.foreach { i =>
    arr(i) = ((a1(i) + a2(i)) * math.random()) + a3(i)
  }

  ArraySeq.unsafeWrapArray(arr)
}

推荐阅读