0 <= x < 2^64 之间的 Scalacheck 数字生成器

Scalacheck number generator between 0 <= x < 2^64

我正在尝试在 C 中纠正一个涵盖 uint64_t 的好数字生成器。这是我目前所拥有的。

def uInt64s : Gen[BigInt] = Gen.choose(0,64).map(pow2(_) - 1)

这是一个好的开始,但它只生成数字 2^n - 1。有没有更有效的方法来生成随机 BigInts,同时保留数字范围 0 <= n < 2^64

使用 ScalaCheck...

从 0..Long.MaxValue 生成数字很容易。

从 0..Long.MaxValue..2^64-1 生成一个 unsigned long 不是那么容易。

尝试过:

❌ Gen.chooseNum(BigInt(0),BigInt(2).pow(64)-1)

不起作用: 此时没有为 BigInt 定义隐式。

❌ Arbitrary.arbBigInt.arbitrary

不起作用:它是 BigInt 类型,但仍然限于有符号 Long 的范围。

✔ 生成一个 Long as BigInt 并任意左移以生成 UINT64

有效: 以 Rickard Nilsson 的 ScalaCheck 代码为指南这通过了测试。

这是我想出的:

// Generate a long and map to type BigInt
def genBigInt : Gen[BigInt] = Gen.chooseNum(0,Long.MaxValue) map (x => BigInt(x))

// Take genBigInt and shift-left a chooseNum(0,64) of positions
def genUInt64 : Gen[BigInt] = for { bi <- genBigInt; n <- Gen.chooseNum(0,64); x = (bi << n) if x >= 0 && x < BigInt(2).pow(64) } yield x

...

// Use the generator, genUInt64()

如前所述,,生成的 BigInts 分布不均匀。首选生成器是@stholzm 解决方案:

def genUInt64b : Gen[BigInt] =
  Gen.chooseNum(Long.MinValue,Long.MaxValue) map (x => 
    BigInt(x) + BigInt(2).pow(63))

它更简单,输入到 ScalaCheck 的数字分布更均匀,速度更快,并且通过了测试。

的更简单、更有效的替代方法如下:

val myGen = {
  val offset = -BigInt(Long.MinValue)
  Arbitrary.arbitrary[Long].map { BigInt(_) + offset }
}
  1. 生成任意Long;
  2. 将其转换为 BigInt;
  3. 添加适当的偏移量,即 -BigInt(Long.MinValue)).

REPL 中的测试:

scala> myGen.sample
res0: Option[scala.math.BigInt] = Some(9223372036854775807)

scala> myGen.sample
res1: Option[scala.math.BigInt] = Some(12628207908230674671)

scala> myGen.sample
res2: Option[scala.math.BigInt] = Some(845964316914833060)

scala> myGen.sample
res3: Option[scala.math.BigInt] = Some(15120039215775627454)

scala> myGen.sample
res4: Option[scala.math.BigInt] = Some(0)

scala> myGen.sample
res5: Option[scala.math.BigInt] = Some(13652951502631572419)

这是我目前的情况,我不是很满意

  /**
    * Chooses a BigInt in the ranges of 0 <= bigInt < 2^^64
    * @return
    */
  def bigInts : Gen[BigInt] = for {
    bigInt <- Arbitrary.arbBigInt.arbitrary
    exponent <- Gen.choose(1,2)
  } yield bigInt.pow(exponent)

  def positiveBigInts : Gen[BigInt] = bigInts.filter(_ >= 0)

  def bigIntsUInt64Range : Gen[BigInt] = positiveBigInts.filter(_ < (BigInt(1) << 64))
  /**
    * Generates a number in the range 0 <= x < 2^^64
    * then wraps it in a UInt64
    * @return
    */
  def uInt64s : Gen[UInt64] = for {
    bigInt <- bigIntsUInt64Range
  } yield UInt64(bigInt)

因为 Arbitrary.argBigInt.arbitrary 似乎只是范围 -2^63 <= x <= 2^63 我花了 x^2 一些时间来获得大于 2^63

的数字

如果您看到可以改进的地方或修复的错误,请免费发表评论

好吧,也许我在这里遗漏了一些东西,但这不是这么简单吗?

def uInt64s : Gen[BigInt] = Gen.chooseNum(Long.MinValue,Long.MaxValue)
                               .map(x => BigInt(x) + BigInt(2).pow(63))

Longs 已经有了正确的位数 - 只需添加 2^63,所以 Long.MinValue 变成 0,Long.MaxValue 变成 2^64 - 1。然后用 BigInt 做加法当然可以。


我很好奇生成值的分布。显然 chooseNum 的分布不均匀,因为它更喜欢特殊值,但 Longs 的边缘情况可能对 UInt64s 也很有趣:

/** Generates numbers within the given inclusive range, with
  *  extra weight on zero, +/- unity, both extremities, and any special
  *  numbers provided. The special numbers must lie within the given range,
  *  otherwise they won't be included. */
def chooseNum[T](minT: T, maxT: T, specials: T*)(