Below is a possible seeded generator that the library could provide. Whilst implementing this I noticed that next
in RandomNumberGenerator
should be mutating
.
public protocol RandomNumberGenerator {
// This determines the functionality for producing a random number.
// Required to implement by all rngs.
mutating func next() -> UInt64 // *Must* be mutating for a seeded generator.
}
/// Swift version of Xorshift+ from: https://en.wikipedia.org/wiki/Xorshift
struct Xorshift128Plus: RandomNumberGenerator {
private var xS: UInt64
private var yS: UInt64
/// Two seeds, `x` and `y`, are required for the random number generator (default values are provided for both).
init(xSeed: UInt64 = 0, ySeed: UInt64 = UInt64.max) {
xS = xSeed == 0 && ySeed == 0 ? UInt64.max : xSeed // Seed cannot be all zeros.
yS = ySeed
}
mutating func next() -> UInt64 {
var x = xS
let y = yS
xS = y
x ^= x << 23 // a
yS = x ^ y ^ (x >> 17) ^ (y >> 26) // b, c
return yS &+ y
}
}
var rng = Xorshift128Plus()
print(rng.next()) // 18446743798831644671
print(rng.next()) // 18446743798823260096
print(rng.next()) // 16140918656667222015
print(rng.next()) // 13835128698895859711
print(rng.next()) // 16140954115928756175
The generator is fast, passes BigCrush even when reversed, and takes up little memory.