我需要从Scala中的列表中随机抽取n个元素的子集,我想知道是否有一种方便的方法可以这样做,而无需手动检查n个元素中的每一个都是唯一的。目前我有这样的事情:
import util.Random
def sample(itms:List[A], sampleSize:Int) {
var numbersSeen = Set[Int]()
var sampled = List[A]()
val itmLen = itms.size()
var sampleIdex = Random.nextInt(itmLen)
while(sampled < sampleSize) {
if(numbersSeen.contains(sampleIdex)){
sampleIdex = Random.nextInt(itmLen)
} else {
numbersSeen.add(sampleIdex)
sampled.add(itms(sampleIdex))
}
}
sampled
}
我希望有更优雅的东西可以在一个范围内生成一个非重复的整数随机列表,或者从一个列表中随机抽样n个元素。
答案 0 :(得分:5)
如果您的列表不是太长,您可以随机播放索引编号列表,然后浏览该列表。
在Scala中会出现类似的情况:
val aList = ('A' to 'Z').toList
val aListIterator = scala.util.Random.shuffle((0 until aList.length).toList).toIterator
然后在你的循环结构中:
...
if( aListIterator.hasNext ) aList(aListIterator.next)
...
如果您的列表很大,那么返回列表大小(用作索引)范围内的唯一随机数的函数可能是更好的方法。 Jeff Preshing最近发表了关于独特随机数的博客http://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers。
答案 1 :(得分:3)
您可以随机选择一个,并从列表中取样,除了您刚刚选择的那个,使用simpleSize-1(tail-)递归:
def sample[A](itms:List[A], sampleSize:Int) = {
def collect(vect: Vector[A], sampleSize: Int, acc : List[A]) : List[A] = {
if (sampleSize == 0) acc
else {
val index = Random.nextInt(vect.size)
collect( vect.updated(index, vect(0)) tail, sampleSize - 1, vect(index) :: acc)
}
}
collect(itms toVector, sampleSize, Nil)
} //> sample: [A](itms: List[A], sampleSize: Int)List[A]
sample(1 to 10 toList, 5) //> res0: List[Int] = List(6, 8, 2, 1, 10)
答案 2 :(得分:1)
itms.map(x => (x, util.Random.nextDouble)).sortBy(_._2).take(sampleSize).map(_._1)
只要你不关心排序的低效率。
答案 3 :(得分:0)
您可以从子集集合中随机抽取样本,即:
val distinctSubsets = itms.to[Set].subsets(sampleSize)
然后随意选择其中一个。
答案 4 :(得分:-1)
这种做法怎么样?
trait RandomOrdering[T] extends Ordering[T]
object RandomOrdering {
implicit def defaultOrdering[T] = new RandomOrdering[T] {
def compare(x:T, y:T) = (Random nextInt 3) - 1
}
}
def sample[A](items:List[A], sampleSize:Int)(implicit r:RandomOrdering[A]) =
items.sorted take sampleSize
它可能性能较差,但它也允许您注入不同的RandomOrdering
。