1

我需要从 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 个元素。

4

5 回答 5

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 。

于 2013-02-14T00:03:13.890 回答
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)
于 2013-02-13T21:51:43.890 回答
1
itms.map(x => (x, util.Random.nextDouble)).sortBy(_._2).take(sampleSize).map(_._1)

只要您不关心排序的低效率。

于 2013-02-13T21:24:20.617 回答
0

您可以从子集中抽取随机样本,即:

val distinctSubsets = itms.to[Set].subsets(sampleSize)

然后随机选择其中之一。

于 2013-02-13T21:02:46.723 回答
-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.

于 2013-02-13T21:11:58.853 回答