Scalacheck数发生器在0 <= x <0之间。 2 ^ 64

时间:2016-06-18 15:45:00

标签: scala scalacheck

我试图纠正一个涵盖C中uint64_t的数字生成器。这是我到目前为止所做的。

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

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

4 个答案:

答案 0 :(得分:4)

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

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 s当然。

我对生成的值的分布感到好奇。显然chooseNum的分布并不统一,因为它更喜欢特殊值,但Longs的边缘情况对于UInt64来说可能也很有趣:

/** 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*)(

答案 1 :(得分:1)

With ScalaCheck...

Generating a number from 0..Long.MaxValue is easy.

Generating an unsigned long from 0..Long.MaxValue..2^64-1 is not so easy.

   Tried:

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

        Does not work: At this time there is not an implicit defined for BigInt.

   ❌ Arbitrary.arbBigInt.arbitrary

       Does not work: It's type BigInt but still limited to the range of signed Long.

   ✔ Generate a Long as BigInt and shift left arbitrarily to make an UINT64

       Works: Taking Rickard Nilsson's, ScalaCheck code as a guide this passed the test.

This is what I came up with:

// 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()

As noted, Scalacheck number generator between 0 <= x < 2^64, the distribution of the BigInts generated is not even. The preferred generator is @stholzm solution:

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

it is simpler, the numbers fed to ScalaCheck will be more evenly distributed, it is faster, and it passes the tests.

答案 2 :(得分:1)

stholmz's answer的更简单,更有效的替代方法如下:

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

答案 3 :(得分:0)

这是我到目前为止所做的,我对此并不满意

  /**
    * 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的数字

如果您发现可以进行地方改进或修复错误,可以免费评论