使用标准库在Scala中计算两个稀疏向量的点积(并生成它们)

时间:2019-07-18 18:11:05

标签: scala dictionary dot-product vectormath

我试图计算Scala中两个稀疏向量的点积(标量积)。我编写的代码正在做我想要做的所有事情,除了将两个向量的相似元素相乘时,它没有考虑0值。

我的答案是72,因为3和18是唯一都不为零的键,它们的计算结果为(3-> 21)+(18-> 51)= 72

我使用withDefaultValue(0)希望它可以“填充”未提及的键/值对,但是我认为情况并非如此,而且我认为这是我的问题之所在。我认为我的问题还可能是“如何使用标准库在Scala中生成稀疏向量”。

如果我输入对应的0,并且两个Map(向量)具有相同数量的键/值对,则我的代码可以正常工作。

```
  val Sparse1 = Map(0 -> 4, 3 -> 7, 6 -> 11, 18 -> 17).withDefaultValue(0)
  val Sparse2 = Map(1 -> 3, 3 -> 3, 11 -> 2,18 -> 3, 20 -> 6).withDefaultValue(0)
  //println(Sparse2.toSeq)//to see what it is....0's missing
  val SparseSum = (Sparse1.toSeq ++ Sparse2.toSeq).groupBy(_._1).mapValues(_.map(_._2).sum)
  //println(SparseSum)
  val productOfValues = ((Sparse1.toSeq ++ Sparse2.toSeq).groupBy(_._1).mapValues(_.map(_._2).reduce(_*_)))
  //println(productOfValues)
  var dotProduct = 0
  for ((h,i) <- productOfValues) {
    dotProduct += i
  }
  //println(dotProduct)
  //If I specify some zero values, lets see what happens:
  val Sparse3 = Map(0 -> 4, 1 -> 0, 3 -> 7, 6 -> 11, 11 -> 0, 18 -> 17, 20 -> 0).withDefaultValue(0)
  val Sparse4 = Map(0 -> 0, 1 -> 3, 3 -> 3, 6 -> 0, 11 -> 2,18 -> 3, 20 -> 6).withDefaultValue(0)
  val productOfValues2 = ((Sparse3.toSeq ++ Sparse4.toSeq).groupBy(_._1).mapValues(_.map(_._2).reduce(_*_)))
  var dotProduct2 = 0
  for ((l, m) <- productOfValues2) {
    dotProduct2 += m
  }
  println(productOfValues2)
  println(dotProduct2)//I get 72

```

我可以以此方式创建稀疏向量,然后更新值

  import scala.collection.mutable.Map
  val Sparse1 = Map[Int, Int]()
  for (k <- 0 to 20) {
    Sparse1 getOrElseUpdate (k, 0)
  }
  val Sparse2 = Map[Int, Int]()
  for (k <- 0 to 20) {
    Sparse2 getOrElseUpdate (k, 0)
  }

但是我想知道是否存在“更好”的方式。我尝试使用“ withDefaultValue(0)”尝试和失败的方式更多

1 个答案:

答案 0 :(得分:1)

由于使用的是稀疏向量,因此可以忽略不在两个向量上的所有键。
因此,我将计算两个键集之间的intersection,然后执行简单的 map-reduce 来计算点积。

type SparseVector[T] = Map[Int, T]

/** Generic function for any type T that can be multiplied & summed. */
def sparseDotProduct[T: Numeric](v1: SparseVector[T], v2: SparseVector[T]): T = {
  import Numeric.Implicits._

  val commonIndexes = v1.keySet & v2.keySet

  commonIndexes
    .map(i => v1(i) * v2(i))
    .foldLeft(implicitly[Numeric[T]].zero)(_ + _)
}

然后,您可以像这样使用它:

// The withDefault(0) is optional now.
val sparse1 = Map(0 -> 4, 3 -> 7, 6 -> 11, 18 -> 17).withDefaultValue(0)
val sparse2 = Map(1 -> 3, 3 -> 3, 11 -> 2, 18 -> 3, 20 -> 6).withDefaultValue(0)

sparseDotProduct(sparse1, sparse2)
// res: Int = 72

编辑-相同的方法,但是没有上下文范围和隐式语法。

type SparseVector[T] = Map[Int, T]

/** Generic function for any type T that can be multiplied & summed. */
def sparseDotProduct[T](v1: SparseVector[T], v2: SparseVector[T])(implicit N: Numeric[T]): T = {      
  val commonIndexes = v1.keySet & v2.keySet

  commonIndexes
    .map(i => N.times(v1(i), v2(i)))
    .foldLeft(N.zero)((acc, element) => N.plus(acc, element))
}

奖金-非备用向量的通用方法。

一个人可以修改上述方法,使其适用于任何种类的载体,而不仅仅是多余的载体。 在这种情况下,我们将需要union个键,并考虑其中一个键不存在的情况。

type MyVector[T] = Map[Int, T]

/** Generic function for any type T that can be multiplied & summed. */
def dotProduct[T: Numeric](v1: MyVector[T], v2: MyVector[T]): T = {
  import Numeric.Implicits._
  val zero = implicitly[Numeric[T]].zero

  val allIndexes = v1.keySet | v2.keySet

  allIndexes.map { i =>
     v1.getOrElse(
       key = i,
       default = zero
     ) * v2.getOrElse(
       key = i,
       default = zero
     )
   }.foldLeft(zero)(_ + _)
}