如何从Scala中的集合中选择一个随机值

时间:2016-01-15 18:59:43

标签: scala

我需要一种从集合中统一选取随机值的方法。

这是我目前的impl。

implicit class TraversableOnceOps[A, Repr](val elements: TraversableOnce[A]) extends AnyVal {
  def pickRandomly : A = elements.toSeq(Random.nextInt(elements.size))
}

但是这段代码实例化了一个新的集合,因此在内存方面并不理想。

有什么方法可以改进吗?

[更新] 使其与Iterator

一起使用
  implicit class TraversableOnceOps[A, Repr](val elements: TraversableOnce[A]) extends AnyVal {
    def pickRandomly : A = {
      val seq = elements.toSeq
      seq(Random.nextInt(seq.size))
    }
  }

4 个答案:

答案 0 :(得分:14)

乍一看,如果不先计算元素,你就可以做到这一点,但你可以做到!

遍历序列 f 并以概率 1 / i 取每个元素 f i

def choose[A](it: Iterator[A], r: util.Random): A =
  it.zip(Iterator.iterate(1)(_ + 1)).reduceLeft((x, y) =>
    if (r.nextInt(y._2) == 0) y else x
  )._1

快速展示统一性:

scala> ((1 to 1000000)
     | .map(_ => choose("abcdef".iterator, r))
     | .groupBy(identity).values.map(_.length))
res45: Iterable[Int] = List(166971, 166126, 166987, 166257, 166698, 166961)

Here's讨论我前一段时间写的数学,虽然我担心它有点不必要地啰嗦。它还推广到选择任何固定数量的元素而不只是一个。

答案 1 :(得分:3)

最简单的方法就是将问题视为使用相等大小的随机数列表压缩集合,然后只提取最大元素。您可以在不实际实现压缩序列的情况下执行此操作。这确实需要遍历整个迭代器,但

val maxElement = s.maxBy(_=>Random.nextInt)

或者,对于隐式版本

implicit class TraversableOnceOps[A, Repr](val elements: TraversableOnce[A]) extends AnyVal {
  def pickRandomly : A = elements.maxBy(_=>Random.nextInt)
}

答案 2 :(得分:0)

可以从集合中随机统一选择元素遍历它而无需复制集合。

以下算法可以解决这个问题:

def choose[A](elements: TraversableOnce[A]): A = {
    var x: A = null.asInstanceOf[A]
    var i = 1

    for (e <- elements) {
        if (Random.nextDouble <= 1.0 / i) {
            x = e
        }

        i += 1
    }

    x
}

算法在每次迭代时都会做出选择:以概率1 / i获取新元素,或保留前一个元素。

要理解为什么算法在随机时统一选择元素,请考虑这一点:首先考虑集合中的元素,例如第一个元素(在本例中,集合只有三个元素)。

迭代:

  1. 概率选择:1。
  2. 概率选择: (在前一次迭代中保持元素的概率)*(保持当前迭代) 概率=&gt; 1 * 1/2 = 1/2
  3. 概率选择:1/2 * 2/3 = 1/3(换句话说,统一)
  4. 如果我们采用另一个元素,例如第二个元素:

    1. 0(无法在此次迭代中选择元素)。
    2. 1/2。
    3. 1 / * 2/3 = 1/3进行。
    4. 最后是第三个:

      1. 0
      2. 0
      3. 1/3。
      4. 这表明该算法随机均匀地选择一个元素。这可以通过归纳法正式证明。

答案 3 :(得分:0)

如果集合足够大以至于您关心实例化,那么这里是常量内存解决方案(我假设它包含整数&#39;但这只对将初始参数传递给<div class="form-group"> <label asp-for="DistrictId" class="col-md-2 control-label"></label> <div class="col-md-10"> <select asp-for="DistrictId" class ="form-control"></select> </div> </div> 而言很重要):

fold

我不确定这是否需要进一步解释......基本上,它与@svenslaggare上面提到的相同,但功能性方面,因为它被标记为scala问题。