I\'m trying to generate seeded random numbers with Swift 4.2+, with the Int.random()
function, however there is no given implementation that allows for the random n
Looks like Swift's implementation of RandomNumberGenerator.next(using:)
changed in 2019. This affects Collection.randomElement(using:)
and causes it to always return the first element if your generator's next()->UInt64
implementation doesn't produce values uniformly across the domain of UInt64
. The GKRandom
solution provided here is therefore problematic because it's next->Int
method states:
* The value is in the range of [INT32_MIN, INT32_MAX].
Here's a solution that works for me using the RNG in Swift's TensorFlow
found here:
public struct ARC4RandomNumberGenerator: RandomNumberGenerator {
var state: [UInt8] = Array(0...255)
var iPos: UInt8 = 0
var jPos: UInt8 = 0
/// Initialize ARC4RandomNumberGenerator using an array of UInt8. The array
/// must have length between 1 and 256 inclusive.
public init(seed: [UInt8]) {
precondition(seed.count > 0, "Length of seed must be positive")
precondition(seed.count <= 256, "Length of seed must be at most 256")
var j: UInt8 = 0
for i: UInt8 in 0...255 {
j &+= S(i) &+ seed[Int(i) % seed.count]
swapAt(i, j)
}
}
// Produce the next random UInt64 from the stream, and advance the internal
// state.
public mutating func next() -> UInt64 {
var result: UInt64 = 0
for _ in 0.. UInt8 {
return state[Int(index)]
}
// Helper to swap elements of the state.
private mutating func swapAt(_ i: UInt8, _ j: UInt8) {
state.swapAt(Int(i), Int(j))
}
// Generates the next byte in the keystream.
private mutating func nextByte() -> UInt8 {
iPos &+= 1
jPos &+= S(iPos)
swapAt(iPos, jPos)
return S(S(iPos) &+ S(jPos))
}
}
Hat tip to my coworkers Samuel, Noah, and Stephen who helped me get to the bottom of this.