Expand a random range from 1–5 to 1–7

前端 未结 30 3147
一个人的身影
一个人的身影 2020-11-22 07:29

Given a function which produces a random integer in the range 1 to 5, write a function which produces a random integer in the range 1 to 7.

  1. What is a simple so
30条回答
  •  温柔的废话
    2020-11-22 08:22

    Here's a solution that fits entirely within integers and is within about 4% of optimal (i.e. uses 1.26 random numbers in {0..4} for every one in {0..6}). The code's in Scala, but the math should be reasonably clear in any language: you take advantage of the fact that 7^9 + 7^8 is very close to 5^11. So you pick an 11 digit number in base 5, and then interpret it as a 9 digit number in base 7 if it's in range (giving 9 base 7 numbers), or as an 8 digit number if it's over the 9 digit number, etc.:

    abstract class RNG {
      def apply(): Int
    }
    
    class Random5 extends RNG {
      val rng = new scala.util.Random
      var count = 0
      def apply() = { count += 1 ; rng.nextInt(5) }
    }
    
    class FiveSevener(five: RNG) {
      val sevens = new Array[Int](9)
      var nsevens = 0
      val to9 = 40353607;
      val to8 = 5764801;
      val to7 = 823543;
      def loadSevens(value: Int, count: Int) {
        nsevens = 0;
        var remaining = value;
        while (nsevens < count) {
          sevens(nsevens) = remaining % 7
          remaining /= 7
          nsevens += 1
        }
      }
      def loadSevens {
        var fivepow11 = 0;
        var i=0
        while (i<11) { i+=1 ; fivepow11 = five() + fivepow11*5 }
        if (fivepow11 < to9) { loadSevens(fivepow11 , 9) ; return }
        fivepow11 -= to9
        if (fivepow11 < to8) { loadSevens(fivepow11 , 8) ; return }
        fivepow11 -= to8
        if (fivepow11 < 3*to7) loadSevens(fivepow11 % to7 , 7)
        else loadSevens
      }
      def apply() = {
        if (nsevens==0) loadSevens
        nsevens -= 1
        sevens(nsevens)
      }
    }
    

    If you paste a test into the interpreter (REPL actually), you get:

    scala> val five = new Random5
    five: Random5 = Random5@e9c592
    
    scala> val seven = new FiveSevener(five)
    seven: FiveSevener = FiveSevener@143c423
    
    scala> val counts = new Array[Int](7)
    counts: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0)
    
    scala> var i=0 ; while (i < 100000000) { counts( seven() ) += 1 ; i += 1 }
    i: Int = 100000000
    
    scala> counts
    res0: Array[Int] = Array(14280662, 14293012, 14281286, 14284836, 14287188,
    14289332, 14283684)
    
    scala> five.count
    res1: Int = 125902876
    

    The distribution is nice and flat (within about 10k of 1/7 of 10^8 in each bin, as expected from an approximately-Gaussian distribution).

提交回复
热议问题