SE-0202: Random Unification

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.

2 Likes