使用 scalacheck 生成排列

发布于 2024-10-19 05:09:46 字数 718 浏览 1 评论 0原文

我有一些像这样的生成器:

val fooRepr = oneOf(a, b, c, d, e)
val foo = for (s <- choose(1, 5); c <- listOfN(s, fooRepr)) yield c.mkString("$")

这会导致重复...我可能会得到两个 a 等。我真正想要的是生成恰好 0 或 1 或每个 a、b、c、d 或 e 的随机排列(至少其中之一),以任何顺序。

我想一定有一个简单的方法,但我正在努力寻找一个困难的方法。 :)

编辑:好的,这似乎有效:

val foo = for (s <- choose(1, 5);
               c <- permute(s, a, b, c, d, e)) yield c.mkString("$")

def permute[T](n: Int, gs: Gen[T]*): Gen[Seq[T]] = {
  val perm = Random.shuffle(gs.toList)
  for {
    is <- pick(n, 1 until gs.size)
    xs <- sequence[List,T](is.toList.map(perm(_)))
  } yield xs
}

...大量借鉴 Gen.pick

谢谢你的帮助,-埃里克

I have some generators like this:

val fooRepr = oneOf(a, b, c, d, e)
val foo = for (s <- choose(1, 5); c <- listOfN(s, fooRepr)) yield c.mkString("$")

This leads to duplicates ... I might get two a's, etc. What I really want is to generate random permutation with exactly 0 or 1 or each of a, b, c, d, or e (with at least one of something), in any order.

I was thinking there must be an easy way, but I'm struggling to even find a hard way. :)

Edited: Ok, this seems to work:

val foo = for (s <- choose(1, 5);
               c <- permute(s, a, b, c, d, e)) yield c.mkString("$")

def permute[T](n: Int, gs: Gen[T]*): Gen[Seq[T]] = {
  val perm = Random.shuffle(gs.toList)
  for {
    is <- pick(n, 1 until gs.size)
    xs <- sequence[List,T](is.toList.map(perm(_)))
  } yield xs
}

...borrowing heavily from Gen.pick.

Thanks for your help, -Eric

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

探春 2024-10-26 05:09:46

Rex,感谢您准确地澄清了我想要做的事情,这是有用的代码,但对于 scalacheck 来说可能不太好,特别是如果所讨论的生成器非常复杂的话。在我的特定情况下,生成器 a、b、c 等生成巨大的字符串。

无论如何,我上面的解决方案有一个错误;对我有用的是下面的。我在 github 上放置了一个小项目来演示如何执行此操作,

其内容如下。如果有更好的方法,我很想知道......

package powerset

import org.scalacheck._
import org.scalacheck.Gen._
import org.scalacheck.Gen
import scala.util.Random

object PowersetPermutations extends Properties("PowersetPermutations") {

  def a: Gen[String] = value("a")

  def b: Gen[String] = value("b")

  def c: Gen[String] = value("c")

  def d: Gen[String] = value("d")

  def e: Gen[String] = value("e")

  val foo = for (s <- choose(1, 5);
                 c <- permute(s, a, b, c, d, e)) yield c.mkString

  def permute[T](n: Int, gs: Gen[T]*): Gen[Seq[T]] = {
    val perm = Random.shuffle(gs.toList)
    for {
      is <- pick(n, 0 until gs.size)
      xs <- sequence[List, T](is.toList.map(perm(_)))
    } yield xs
  }

  implicit def arbString: Arbitrary[String] = Arbitrary(foo)

  property("powerset") = Prop.forAll {
    a: String => println(a); true
  }
}

谢谢,
埃里克

Rex, thanks for clarifying exactly what I'm trying to do, and that's useful code, but perhaps not so nice with scalacheck, particularly if the generators in question are quite complex. In my particular case the generators a, b, c, etc. are generating huge strings.

Anyhow, there was a bug in my solution above; what worked for me is below. I put a tiny project demonstrating how to do this at github

The guts of it is below. If there's a better way, I'd love to know it...

package powerset

import org.scalacheck._
import org.scalacheck.Gen._
import org.scalacheck.Gen
import scala.util.Random

object PowersetPermutations extends Properties("PowersetPermutations") {

  def a: Gen[String] = value("a")

  def b: Gen[String] = value("b")

  def c: Gen[String] = value("c")

  def d: Gen[String] = value("d")

  def e: Gen[String] = value("e")

  val foo = for (s <- choose(1, 5);
                 c <- permute(s, a, b, c, d, e)) yield c.mkString

  def permute[T](n: Int, gs: Gen[T]*): Gen[Seq[T]] = {
    val perm = Random.shuffle(gs.toList)
    for {
      is <- pick(n, 0 until gs.size)
      xs <- sequence[List, T](is.toList.map(perm(_)))
    } yield xs
  }

  implicit def arbString: Arbitrary[String] = Arbitrary(foo)

  property("powerset") = Prop.forAll {
    a: String => println(a); true
  }
}

Thanks,
Eric

染年凉城似染瑾 2024-10-26 05:09:46

您不是在描述排列,而是描述幂集(减去空集)编辑:您正在描述幂集和排列的组合。索引集合 N 的幂集与 2^N 同构,因此我们简单地(仅在 Scala 中;也许您想更改它以便与 ScalaCheck 一起使用):

def powerSet[X](xs: List[X]) = {
  val xis = xs.zipWithIndex
  (for (j <- 1 until (1<<xs.length)) yield {
    for ((x,i) <- xis if ((j & (1<<i)) != 0)) yield x
  }).toList
}

生成给定集合的所有可能子集。当然,如果原始集合包含多个元素,则显式生成幂集是不明智的。如果您不想生成所有这些,只需传入从 1(1<<(xs.length-1)) 的随机数并运行内循环。 (如果有 33-64 个元素,则切换到 Long;如果还有更多元素,则切换到 BitSet。)然后,如果您愿意,可以对结果进行排列以切换顺序。


编辑:如果您可以轻松生成排列并且可以添加虚拟参数,那么还有另一种方法可以做到这一点:使用 Stop 标记使您的列表更长。然后排列并 .takeWhile(_ != Stop)。哒哒!任意长度的排列。 (如果需要的话,过滤掉零长度答案。)

You're not describing a permutation, but the power set (minus the empty set)Edit: you're describing a combination of a power set and a permutation. The power set of an indexed set N is isomorphic to 2^N, so we simply (in Scala alone; maybe you want to alter this for use with ScalaCheck):

def powerSet[X](xs: List[X]) = {
  val xis = xs.zipWithIndex
  (for (j <- 1 until (1<<xs.length)) yield {
    for ((x,i) <- xis if ((j & (1<<i)) != 0)) yield x
  }).toList
}

to generate all possible subsets given a set. Of course, explicit generation of power sets is unwise if they original set contains more than a handful of elements. If you don't want to generate all of them, just pass in a random number from 1 until (1<<(xs.length-1)) and run the inner loop. (Switch to Long if there are 33-64 elements, and to BitSet if there are more yet.) You can then permute the result to switch the order around if you wish.


Edit: there's another way to do this if you can generate permutations easily and you can add a dummy argument: make your list one longer, with a Stop token. Then permute and .takeWhile(_ != Stop). Ta-da! Permutations of arbitrary length. (Filter out the zero-length answer if need be.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文