我需要一种从集合中统一选取随机值的方法。
这是我目前的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))
}
}
答案 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
获取新元素,或保留前一个元素。
要理解为什么算法在随机时统一选择元素,请考虑这一点:首先考虑集合中的元素,例如第一个元素(在本例中,集合只有三个元素)。
迭代:
如果我们采用另一个元素,例如第二个元素:
最后是第三个:
这表明该算法随机均匀地选择一个元素。这可以通过归纳法正式证明。
答案 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问题。