我正在尝试使用Int.random()
函数用Swift 4.2+生成种子随机数,但是没有给定的实现允许种子随机数生成器。据我所知,唯一的方法是创建一个符合RandomNumberGenerator
协议的新随机数生成器。有没有人建议采用更好的方法,或者是否有符合RandomNumberGenerator的类且具有播种功能的实现以及如何实现?
此外,在寻找解决方案时,我看到两次提到过srand
和drand
两个函数,但是从很少提及的角度来看,我不确定使用它是不好的约定,我也找不到关于它们的任何文档。
我正在寻找最简单的解决方案,不一定是最安全或最快的性能解决方案(例如,使用外部库并不理想)。
更新:“播种”是指我将种子传递给随机数生成器,以便将同一种子传递给两个不同的设备或在两个不同的时间传递,生成器将产生相同的数字。目的是我为应用程序随机生成数据,而不是将所有数据保存到数据库中,而是想保存种子,并在每次用户加载应用程序时使用该种子重新生成数据。
答案 0 :(得分:2)
这是RPatel99答案的替代方法,它说明了GKRandom值范围。
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
}
答案 1 :(得分:1)
我最终使用 srand48()
和 drand48()
为特定测试生成了一个带有种子的伪随机数。
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())
}
}
因此,在生产中实现使用 Android documents,但在测试套件中使用 SeededRandomNumberGenerator
。
示例:
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"
答案 2 :(得分:0)
因此,我根据Martin R的建议使用了GamePlayKit
的{{1}}来创建一个符合RandomNumberGenerator协议的类,我可以使用{{1} }:
GKMersenneTwisterRandomSource
用法:
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()))
}
}
的种子功能和标准库的随机函数(如数组的// 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.
和GKMersenneTwisterRandomSource
的简单性)给了我所需的灵活性和轻松实现。 Int,Bool,Double等)
答案 3 :(得分:0)
类似于Swift的RandomNumberGenerator.next(using:)
changed in 2019的实现。如果生成器的Collection.randomElement(using:)
实现在next()->UInt64
的整个域中生成的值不一致,这会影响UInt64
并使它始终返回第一个元素。因此,此处提供的GKRandom
解决方案是有问题的,因为它是next->Int
方法状态:
* The value is in the range of [INT32_MIN, INT32_MAX].
以下是在Swift TensorFlow
发现的here中使用RNG的解决方案:
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))
}
}
向我的同事塞缪尔(Samuel),诺亚(Noah)和史蒂芬(Stephen)的帽子提示,帮助我深入了解了这一点。
答案 4 :(得分:-5)
var g = SystemRandomNumberGenerator()
lazy private var random1 = Double.random(in: 0..<1, using: &self.g)
参考号:https://developer.apple.com/documentation/swift/systemrandomnumbergenerator