Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 543 Vote(s) - 3.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Swift 4.2+ seeding a random number generator

#1
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 number generator to be seeded. As far as I can tell, the only way to do this is to create a new random number generator that conforms to the `RandomNumberGenerator` protocol. Does anyone have a recommendation for a better way to do it, or an implementation of a RandomNumberGenerator conforming class that has the functionality of being seeded, and how to implement it?

Also, I have seen two functions `srand` and `drand` mentioned a couple times while I was looking for a solution to this, but judging by how rarely it was mentioned, I'm not sure if using it is bad convention, and I also can't find any documentation on them.

I'm looking for the simplest solution, not necessarily the most secure or fastest performance one (e.g. using an external library would not be ideal).

**Update:** By "seeded", I mean that I was to pass in a seed to the random number generator so that if I pass in the same seed to two different devices or at two different times, the generator will produce the same numbers. The purpose is that I'm randomly generating data for an app, and rather than save all that data to a database, I want to save the seed and regenerate the data with that seed every time the user loads the app.
Reply

#2
The `srand48` implementations didn't work for me when I tried them with `Bool.random(using:)`. They produced:
```
var randomNumberGenerator = RandomNumberGeneratorWithSeed(seed:69)
for _ in 0..<100 {
print("\(Bool.random(using: &randomNumberGenerator))")
}
true
true
false
false
true
true
false
false
true
true
...
```

However, in the Swift Forums, I found a [post](

[To see links please register here]

) from [Nate Cook](

[To see links please register here]

) with a Swift implementation of a public domain algorithm that appears more random in my test above (no obvious pattern exists)

```
// This is a fixed-increment version of Java 8's SplittableRandom generator.
// It is a very fast generator passing BigCrush, with 64 bits of state.
// See

[To see links please register here]

and
//

[To see links please register here]

//
// Derived from public domain C implementation by Sebastiano Vigna
// See

[To see links please register here]

public struct SplitMix64: RandomNumberGenerator {
private var state: UInt64

public init(seed: UInt64) {
self.state = seed
}

public mutating func next() -> UInt64 {
self.state &+= 0x9e3779b97f4a7c15
var z: UInt64 = self.state
z = (z ^ (z &>> 30)) &* 0xbf58476d1ce4e5b9
z = (z ^ (z &>> 27)) &* 0x94d049bb133111eb
return z ^ (z &>> 31)
}
}
```
Reply

#3
Simplified version for Swift 5:

struct RandomNumberGeneratorWithSeed: RandomNumberGenerator {
init(seed: Int) { srand48(seed) }
func next() -> UInt64 { return UInt64(drand48() * Double(UInt64.max)) }
}
@State var seededGenerator = RandomNumberGeneratorWithSeed(seed: 123)
// when deployed used seed: Int.random(in: 0..<Int.max)

Then to use it:

let rand0to99 = Int.random(in: 0..<100, using: &seededGenerator)
Reply

#4
I ended up using `srand48()` and `drand48()` to generate a pseudo-random number with a seed for a specific test.

```swift
class SeededRandomNumberGenerator : RandomNumberGenerator {

let range: ClosedRange<Double> = Double(UInt64.min) ... Double(UInt64.max)

init(seed: Int) {
// srand48() — Pseudo-random number initializer
srand48(seed)
}

func next() -> UInt64 {
// drand48() — Pseudo-random number generator
return UInt64(range.lowerBound + (range.upperBound - range.lowerBound) * drand48())
}

}
```

So, in production the implementation uses the [`SystemRandomNumberGenerator`](

[To see links please register here]

) but in the test suite it uses the `SeededRandomNumberGenerator`.

**Example:**

```swift
let messageFixtures: [Any] = [
"a string",
["some", ["values": 456]],
]

var seededRandomNumberGenerator = SeededRandomNumberGenerator(seed: 13)

func randomMessageData() -> Any {
return messageFixtures.randomElement(using: &seededRandomNumberGenerator)!
}

// Always return the same element in the same order
randomMessageData() //"a string"
randomMessageData() //["some", ["values": 456]]
randomMessageData() //["some", ["values": 456]]
randomMessageData() //["some", ["values": 456]]
randomMessageData() //"a string"
```
Reply

#5
Looks like Swift's implementation of `RandomNumberGenerator.next(using:)` [changed in 2019][1]. 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][2]:

```

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..<UInt64.bitWidth / UInt8.bitWidth {
result <<= UInt8.bitWidth
result += UInt64(nextByte())
}
print(result)
return result
}

// Helper to access the state.
private func S(_ index: UInt8) -> 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.

[1]:
[2]:

[To see links please register here]

Reply

#6
Here's alternative to the answer from RPatel99 that accounts GKRandom values range.

```swift
import GameKit

struct ArbitraryRandomNumberGenerator : RandomNumberGenerator {

mutating func next() -> UInt64 {
// GKRandom produces values in [INT32_MIN, INT32_MAX] range; hence we need two numbers to produce 64-bit value.
let next1 = UInt64(bitPattern: Int64(gkrandom.nextInt()))
let next2 = UInt64(bitPattern: Int64(gkrandom.nextInt()))
return next1 ^ (next2 << 32)
}

init(seed: UInt64) {
self.gkrandom = GKMersenneTwisterRandomSource(seed: seed)
}

private let gkrandom: GKRandom
}
```
Reply

#7
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.)
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through