In the beginning of this year I blogged about how to generate random float and integers. Meanwhile, SE-0202 “Random Unification” got implemented in Swift 4.2 making my additions unnecessary. Let’s take a look how to use new API for getting random numbers.
Random integers, floats and booleans
In my own implementation I was extending range with a function named random(). Swift 4.2 took different path. Instead, it adds functions like static func random(in range: Range, using generator: inout T) -> UInt64 where T : RandomNumberGenerator
to FixedWidthInteger, BinaryFloatingPoint and Bool. By default, SystemRandomNumberGenerator with cryptographically secure implementation is used as a generator. But for example if it is required to seed the generator, custom type needs to be created. This might be useful in testing where every execution should produce the same results.
In addition to getting random numbers, there is now API for shuffling a collection or getting a random element. As collections can be empty, the randomElement()
function returns optional.
let double = Double.random(in: -1.2...4.5) | |
let integer = Int.random(in: .min ... .max) | |
let unsignedInteger = UInt.random(in: 4...9) | |
let bool = Bool.random() | |
var generator = SystemRandomNumberGenerator() | |
let double2 = Double.random(in: 1.0..<4.2, using: &generator) | |
let array = ["Cat", "Cow", "Dog", "Sheep"] | |
if let element = array.randomElement() { | |
print(element) // e.g Dog | |
} | |
let shuffled = array.shuffled() | |
print(array, shuffled) // e.g ["Cat", "Cow", "Dog", "Sheep"] ["Dog", "Cow", "Sheep", "Cat"] |
Seeded random number generator
As I mentioned before, sometimes it is needed to seed random number generator. Creating a generator what uses a seed is quite simple. First, we need a random number function what supports seeding. Therefore arcrandom()
and arcrandom_uniform()
are not usable in this use case. What we can use is srand48()
for seeding and drand48()
for getting a random double in range of 0 to 1. Using this, we can implement the required method in the RandomNumberGenerator protocol.
struct SeededRandomNumberGenerator: RandomNumberGenerator { | |
init(seed: Int) { | |
srand48(seed) | |
} | |
func next() -> UInt64 { | |
return UInt64(drand48() * Double(UInt64.max)) | |
} | |
} | |
var seededGenerator = SeededRandomNumberGenerator(seed: 5) | |
let random = Int.random(in: -5...5, using: &seededGenerator) |
Summary
We took a look at convenient and flexible APIs for getting and using random numbers in Swift 4.2. In addition, we created a seeded random number generator what might be useful in testing.
Playground
RandomUnification (GitHub) Xcode 10, Swift 4.2
References
SE-0202 (Apple)
RandomNumberGenerator (Apple)
srand48 (linux man pages)