I was learning random number generation with swift and Gameplaykit. In the following code, are the numbers generated when I initialize the randomDist and it simply gives a sample from the already generated numbers when I call nextInt or is the random generator lazy-generated when I call nextInt()?
let rand = GKMersenneTwisterRandomSource() // the generator can be specified
let randomDist = GKRandomDistribution(randomSource: rand, lowestValue: 50, highestValue: 100)
randomDist.nextInt()
Remember that a GKRandomDistribution
can utilize any underlying randomizer — that is, not just any of the GameplayKit GKRandomSource
classes, but any class that implements the GKRandom
protocol. So you can answer this question for yourself by implementing your own random source and seeing how/when its methods get called.
class LoggingRandomSource: GKRandom {
let source = GKARC4RandomSource()
@objc func nextInt() -> Int {
let num = source.nextInt()
print("nextInt: \(num)")
return num
}
@objc func nextIntWithUpperBound(upperBound: Int) -> Int {
let num = source.nextIntWithUpperBound(upperBound)
print("nextIntWithUpperBound: \(num)")
return num
}
@objc func nextUniform() -> Float {
let num = source.nextUniform()
print("nextUniform: \(num)")
return num
}
@objc func nextBool() -> Bool {
let flip = source.nextBool()
print("nextBool: \(flip)")
return flip
}
}
let rand = LoggingRandomSource()
let randomDist = GKRandomDistribution(randomSource: rand, lowestValue: 50, highestValue: 100)
randomDist.nextInt()
Keep exploring with this trick you'll notice a few things about the random distribution classes:
GKRandomDistribution
calls nextIntWithUpperBound
on the underlying random source once for each call to one of its methods. This makes sense, because the underlying source's nextIntWithUpperBound
is assumed to be uniform, so all that GKRandomDistribution
needs to do is map that uniform int to its lowestValue
-highestValue
range.
GKGaussianDistribution
makes two calls to the underlying nextUniform
for each call to one of its methods. That's because there are lots of ways to generate a Gaussian (aka normal) random value given two uniform random values — see the Box-Muller transform.
GKShuffledDistribution
makes a bunch of calls to the underlying nextIntWithUpperBound
the first time you ask it for a number, but you can ask it for many more without it calling on the underlying source again. This fits with that distribution's documented behavior: it makes sure to exhaust all possible values in its range before repeating the same one again. One easy way to do that is to take all of those values, shuffle their order, and then pull a value from the shuffled pool on each call until it empties.