【问题标题】:I want multiple non-random numbers in swift我想要多个非随机数 swift
【发布时间】:2015-05-31 19:45:33
【问题描述】:

我正在使用 swift,并希望在我的游戏中拥有许多可复制的模式。

理想情况下,我会有某种类似这样工作的共享类(这是一种伪 Swift 代码):

class RandomNumberUtility {
    static var sharedInstance = RandomNumberUtility()
    var random1 : Random()
    var random2 : Random()

    func seedRandom1(seed : Int){
        random1 = Random(seed)
    }
    func seedRandom2(seed : Int){
        random2 = Random(seed)
    }
    func getRandom1() -> Int {
        return random1.next(1,10)
    }
    func getRandom2() -> Int {
        return random2.next(1,100)
    }
}

然后,开始这个系列,在我的程序中的任何地方我都可以这样:

RandomNumberUtility.sharedInstance.seedNumber1(7)
RandomNumberUtility.sharedInstance.seedNumber2(12)

然后我会知道(例如)我打电话的前 4 次

RandomNumberUtility.sharedInstance.getRandom1()

我总是会得到相同的值(例如:6、1、2、6) 这将一直持续到某个时候我再次播种该数字,然后我会得到完全相同的系列(如果我使用相同的种子)或不同的系列(如果我使用不同的种子)。

我想同时拥有多个数字系列(随机 1 和随机 2)。

我不知道如何开始把它变成一个真正的 Swift 类。

【问题讨论】:

    标签: ios swift random


    【解决方案1】:

    这是一个可能的实现。它使用jrand48 伪随机数生成器, 它产生 32 位数字。 这个PRNG不如arc4random(),但是有优势 它的所有状态都存储在用户提供的数组中,因此多个 实例可以独立运行。

    struct RandomNumberGenerator {
        // 48 bit internal state for jrand48()
        private var state : [UInt16] = [0, 0, 0]
    
        // Return pseudo-random number in the range 0 ... upper_bound-1:
        mutating func next(upper_bound: UInt32) -> UInt32 {
    
            // Implementation avoiding the "module bias" problem,
            // taken from: http://stackoverflow.com/a/10989061/1187415,
            // Swift translation here: http://stackoverflow.com/a/26550169/1187415
    
            let range = UInt32.max - UInt32.max % upper_bound
            var rnd : UInt32
            do {
                rnd = UInt32(truncatingBitPattern: jrand48(&state))
            } while rnd >= range
    
            return rnd % upper_bound
        }
    
        mutating func seed(newSeed : Int) {
            state[0] = UInt16(truncatingBitPattern: newSeed)
            state[1] = UInt16(truncatingBitPattern: (newSeed >> 16))
            state[2] = UInt16(truncatingBitPattern: (newSeed >> 32))
        }
    }
    

    例子:

    var rnd1 = RandomNumberGenerator()
    rnd1.seed(7)
    var rnd2 = RandomNumberGenerator()
    rnd2.seed(12)
    
    println(rnd1.next(10)) // 2
    println(rnd1.next(10)) // 8
    println(rnd1.next(10)) // 1
    
    println(rnd2.next(10)) // 6
    println(rnd2.next(10)) // 0
    println(rnd2.next(10)) // 5
    

    如果rnd1 的种子值与上面相同,那么它 再次产生相同的数字:

    rnd1.seed(7)
    println(rnd1.next(10)) // 2
    println(rnd1.next(10)) // 8
    println(rnd1.next(10)) // 1
    

    【讨论】:

      【解决方案2】:

      您需要的是一个生成伪随机数的单例,并确保所有需要通过此类调用随机数的代码。诀窍是为每次运行代码重置种子。这是一个简单的 RandomGenerator 类,它将为您解决问题(它针对速度进行了优化,这在编写游戏时是一件好事):

      import Foundation
      
      // This random number generator comes from: Klimov, A. and Shamir, A.,
      // "A New Class of Invertible Mappings", Cryptographic Hardware and Embedded
      // Systems 2002, http://dl.acm.org/citation.cfm?id=752741
      //
      // Very fast, very simple, and passes Diehard and other good statistical
      // tests as strongly as cryptographically-secure random number generators (but
      // is not itself cryptographically-secure).
      
      class RandomNumberGenerator {
      
          static let sharedInstance = RandomNumberGenerator()
      
          private init(seed: UInt64 = 12347) {
              self.seed = seed
          }
      
          func nextInt() -> Int {
              return next(32)
          }
      
          private func isPowerOfTwo(x: Int) -> Bool { return x != 0 && ((x & (x - 1)) == 0) }
      
          func nextInt(max: Int) -> Int {
              assert(!(max < 0))
      
              // Fast path if max is a power of 2.
              if isPowerOfTwo(max) {
                  return Int((Int64(max) * Int64(next(31))) >> 31)
              }
      
              while (true) {
                  var rnd = next(31)
                  var val = rnd % max
                  if rnd - val + (max - 1) >= 0 {
                      return val
                  }
              }
          }
      
          func nextBool() -> Bool {
              return next(1) != 0
          }
      
          func nextDouble() -> Double {
              return Double((Int64(next(26)) << 27) + Int64(next(27))) /
                  Double(Int64(1) << 53)
          }
      
          func nextInt64() -> Int64 {
              let lo = UInt(next(32))
              let hi = UInt(next(32))
              return Int64(UInt64(lo) | UInt64(hi << 32))
          }
      
          func nextBytes(inout buffer: [UInt8]) {
              for n in 0..<buffer.count {
                  buffer[n] = UInt8(next(8))
              }
          }
      
          var seed: UInt64 {
              get {
                  return _seed
              }
              set(seed) {
                  _initialSeed = seed
                  _seed = seed
              }
          }
      
          var initialSeed: UInt64 {
              return _initialSeed!
          }
      
          private func randomNumber() -> UInt32 {
              _seed = _seed &+ ((_seed &* _seed) | 5)
              return UInt32(_seed >> 32)
          }
      
          private func next(bits: Int) -> Int {
              assert(bits > 0)
              assert(!(bits > 32))
              return Int(randomNumber() >> UInt32(32 - bits))
          }
      
          private var _initialSeed: UInt64?
          private var _seed: UInt64 = 0
      }
      

      【讨论】:

      • 我做了一些性能测试,结果发现这比使用jrand48()要慢。我无法解释原因,也无法判断这两种方法的质量,但是(除非我犯了一些错误)在 [0,1000] 范围内生成 1,000,000 个随机数用我建议的方法用了 6 毫秒,用你的方法用了 21 毫秒(编译在 Mac Book Pro 上处于发布模式)。
      • 如果上限是 2 的幂(1024 而不是 1000),那么你的更快:3 ms(你的方法)vs 6 ms(我的方法)。
      • while(true) 捕获超出范围的值可能会导致此问题。这是一个黑客。我想不出比重试更好的方法,直到感觉在界限内。在这种情况下,最好请求双 0..
      猜你喜欢
      • 1970-01-01
      • 2015-03-26
      • 1970-01-01
      • 2017-10-11
      • 2018-05-01
      • 2014-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多