Swift 4.2+ Seeding a Random Number Generator

Swift 4.2+ seeding a random number generator

So I used Martin R's suggestion to use GamePlayKit's GKMersenneTwisterRandomSource to make a class that conformed to the RandomNumberGenerator protocol, which I was able to use an instance of in functions like Int.random():

import GameplayKit

class SeededGenerator: RandomNumberGenerator {
let seed: UInt64
private let generator: GKMersenneTwisterRandomSource
convenience init() {
self.init(seed: 0)
}
init(seed: UInt64) {
self.seed = seed
generator = GKMersenneTwisterRandomSource(seed: seed)
}
func next<T>(upperBound: T) -> T where T : FixedWidthInteger, T : UnsignedInteger {
return T(abs(generator.nextInt(upperBound: Int(upperBound))))
}
func next<T>() -> T where T : FixedWidthInteger, T : UnsignedInteger {
return T(abs(generator.nextInt()))
}
}

Usage:

// Make a random seed and store in a database
let seed = UInt64.random(in: UInt64.min ... UInt64.max)
var generator = Generator(seed: seed)
// Or if you just need the seeding ability for testing,
// var generator = Generator()
// uses a default seed of 0

let chars = ['a','b','c','d','e','f']
let randomChar = chars.randomElement(using: &generator)
let randomInt = Int.random(in: 0 ..< 1000, using: &generator)
// etc.

This gave me the flexibility and easy implementation that I needed by combining the seeding functionality of GKMersenneTwisterRandomSource and the simplicity of the standard library's random functions (like .randomElement() for arrays and .random() for Int, Bool, Double, etc.)

Is there're a way to seed Swift 4.2 random number generator

The whole idea of the new architecture is that any generator can be substituted just by adopting the RandomNumberGenerator protocol. So if you need a repeatable seed, use your own random generator algorithm.

How to seed the Int.random method call in Swift?

See https://developer.apple.com/documentation/swift/systemrandomnumbergenerator

SystemRandomNumberGenerator is automatically seeded. If you want to use your own seeded RNG, you'll have to implement one yourself, conforming to the RandomNumberGenerator protocol.

How to generate a random number in Swift?

Swift 4.2+

Swift 4.2 shipped with Xcode 10 introduces new easy-to-use random functions for many data types.
You can call the random() method on numeric types.

let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()

Swift - Seeding arc4random_uniform? Or alternative?

I know "GameKit" sounds like it's just for games, but it contains a serious random number generation system. I suggest you take a look at GKMersenneTwisterRandomSource and GKRandomDistribution. The GKMersenneTwisterRandomSource takes a random seed (if you so choose) and the GKRandomDistribution class implements a Uniform Distribution. Used together, they do exactly what you're looking for.

import GameKit

// The Mersenne Twister is a very good algorithm for generating random
// numbers, plus you can give it a seed...
let rs = GKMersenneTwisterRandomSource()
rs.seed = 1780680306855649768

// Use the random source and a lowest and highest value to create a
// GKRandomDistribution object that will provide the random numbers.
let rd = GKRandomDistribution(randomSource: rs, lowestValue: 0, highestValue: 100)

// Now generate 10 numbers in the range 0...100:
for _ in 1...10 {
print(rd.nextInt())
}

print("---")

// Let's set the seed back to the starting value, and print the same 10
// random numbers.
rs.seed = 1780680306855649768
for _ in 1...10 {
print(rd.nextInt())
}

Generating random numbers with Swift

let randomIntFrom0To10 = Int.random(in: 1..<10)
let randomFloat = Float.random(in: 0..<1)

// if you want to get a random element in an array
let greetings = ["hey", "hi", "hello", "hola"]
greetings.randomElement()

What is the equivalent of seeded random in Swift3 (Xcode8 beta 1)

You can use
srand48(seed) and drand48() in Swift3.

Random Number generation with GameplayKit

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.

Does rand() indeed produce a random value when the random number generator has been seeded?

With the traditional definition of a pseudorandom generator, if you know what the generator has been seeded with, then the sequence of output values is completely determined and not random. This means that if you knew the seed for a random generator, then you could predict every single output that generator would produce from that point forward. (A good random number generator is one where seeing a sequence of outputs of the generator does not let you easily reverse-engineer what the random seed is or predict other values.)

I seem to remember reading a while back that, a while back, some popular poker websites were not doing a good job choosing their random seeds. Some people figured out that you could input the pattern of cards you were seeing, and the system could then reverse-engineer the random seed and let you predict all the future cards. Oops. These days, we have cryptographically secure pseudorandom generators based on encryption routines that, at least when it comes to what's known in the open literature, can't be predicted even if you have gigabytes of random bits of output from the generators.

If you do need to get something that really isn't predictable - that is, you want to get a bunch of truly random bits - you'll need to use something other than a pseudorandom number generator. Most operating systems have some mechanism in place to generate values that do appear to be truly random. They might, for example, look at how long it takes for different capacitors to discharge on the motherboard, or factor in timing information from a clock, or see how the user interacts with the keyboard, etc. These data can be fed into something called an entropy accumulator that slowly builds up more and more random bits. If you need a value that's truly random and can't be predicted in advance, you can check your particular OS for the mechanism used to get data from the entropy accumulator. (You can read from /dev/random on UNIX-style machines, for example.)

Often, pulling data from the entropy accumulator takes time, since the computer has to wait long enough for enough different sources to mix together to give you back high-quality random data. A common strategy, therefore, is to use the entropy accumulator to get a high-quality random seed, then "stretch" the randomness by using it as the seed of a strong pseudorandom generator.



Related Topics



Leave a reply



Submit