【问题标题】:How do you generate a shuffled sequence with ScalaCheck?您如何使用 ScalaCheck 生成混洗序列?
【发布时间】:2016-11-28 18:30:52
【问题描述】:

我一直在尝试使用 scalacheck 生成混洗序列。 Scalacheck 没有提供任何生成器来直接完成,我在网上找不到任何简单的答案。经过一番思考,下面是我的做法。我希望其他人会发现有用。

【问题讨论】:

  • SO 不是展示答案的地方。在 GitHub 或类似的地方创建一个 gist。
  • 自我回答问题are fine——这不是一个很好的例子。问题应该被完整地陈述出来,答案应该不仅仅是一大块格式笨拙的代码。
  • @TravisBrown 我按照您的建议编辑了问题以清楚地说明它。至于代码,我看不出格式有多尴尬。在所有情况下,我都写了这个问题和答案,因为我一直在寻找它并且找不到它。任何在未来寻找它的人都可能很高兴在未来找到它。这是对有效问题的简洁但正确的答案,因此很有价值。对我来说,反对票似乎过分了。

标签: scala scalacheck


【解决方案1】:

您可以使用集合混洗算法(例如 scala.util.Random 中的算法)来获得更清晰的解决方案:

/**
 * This represents a potential list shuffling. It
 * acts as a pure function - when applied to the
 * same list it always returns the same result
 */
class Shuffling(seed: Int) {
  def apply[T](xs: Seq[T]): Seq[T] = {
    val r = new scala.util.Random(seed)
    r.shuffle(xs)
  }
}

import org.scalacheck.Arbitrary._
import org.scalacheck.Gen

val shufflingGen: Gen[Shuffling] = arbitrary[Int].map(new Shuffling(_))

上面的 sn-p 定义了一个shuffling - 一个可能的列表重新排序。它还定义了一个提供任意改组实例的生成器。以下示例展示了如何使用 shuffle 对整数范围进行重新排序:

def shuffledRange(n: Int): Gen[Seq[Int]] = {
  for {
    shuffling <- shufflingGen
  } yield shuffling(Range(0, n))
}

Shuffling 类的形式添加一层间接性可能看起来很奇怪——我们也可以直接在列表中应用Random.shuffle。这是因为除了 Scalacheck 已经在后台执行的隐式随机化之外,我们还希望避免额外的随机源 - 改组中的随机源来自 scalacheck 生成器。

这样做的一个好处是生成案例的可重复性(无论何时 Scalacheck will support it)。

【讨论】:

    【解决方案2】:

    org.scalacheck.Gen.pick(n: Int, l: Iterable[T]): Gen[Seq[T]] 以随机顺序从l 中挑选n 不同的元素。用n = l.length调用pick,你会随机生成一个

    【讨论】:

    • 这不起作用。我想要一个以随机顺序包含 1 和 n(也是随机)之间的整数的列表,我编写了以下生成器,但它只生成升序数字: Gen.choose(0, 10).flatMap(len => Gen .pick(len, 1 to len))
    • 目前不按随机顺序选择看到这个issue
    • 你是对的。我认为最好的解决方案是复制到一个数组,并通过生成向量 [rand(length), rand(length - 1), rand(length - 2), ... ] 随机交换索引与 scalacheck 并使用这些索引交换数组的连续元素。然后转换回 Seq。我有一段时间没看 Scala 了。您愿意将此作为解决方案吗?
    • 顺便说一下,这被称为 Knuth 或 Fisher-Yates Shuffle:en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
    【解决方案3】:
    def shuffle[T](s: Seq[T], prefix: Seq[T] = Vector()): Gen[Seq[T]] =
        if (s.length == 0) Gen.const(prefix.toSeq)
        else
          Gen.choose(0, s.length - 1)
            .flatMap { i => 
              shuffle(
                s.take(i) ++ s.takeRight(s.length - i - 1), 
                prefix :+ s(i)) }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-02
      • 1970-01-01
      • 2011-10-20
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多