Swift 4.2+播种随机数生成器

时间:2019-02-22 06:56:43

标签: swift random swift4.2 random-seed

我正在尝试使用Int.random()函数用Swift 4.2+生成种子随机数,但是没有给定的实现允许种子随机数生成器。据我所知,唯一的方法是创建一个符合RandomNumberGenerator协议的新随机数生成器。有没有人建议采用更好的方法,或者是否有符合RandomNumberGenerator的类且具有播种功能的实现以及如何实现?

此外,在寻找解决方案时,我看到两次提到过sranddrand两个函数,但是从很少提及的角度来看,我不确定使用它是不好的约定,我也找不到关于它们的任何文档。

我正在寻找最简单的解决方案,不一定是最安全或最快的性能解决方案(例如,使用外部库并不理想)。

更新:“播种”是指我将种子传递给随机数生成器,以便将同一种子传递给两个不同的设备或在两个不同的时间传递,生成器将产生相同的数字。目的是我为应用程序随机生成数据,而不是将所有数据保存到数据库中,而是想保存种子,并在每次用户加载应用程序时使用该种子重新生成数据。

5 个答案:

答案 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