【问题标题】:What is the equivalent of seeded random in Swift3 (Xcode8 beta 1)Swift 3(Xcode 8 beta 1)中的种子随机数是什么
【发布时间】:2016-06-17 03:42:17
【问题描述】:

我需要在每次执行我的应用时启动相同的随机数列表。 srand/rand 不再存在。那我该怎么办?

private extension Array {
    private func randomValues(_ seed: UInt32, num: Int) -> [Element] {
        srand (seed)

        var indices = [Int]()
        indices.reserveCapacity(num)
        let range = 0..<self.count
        for _ in 0..<num {
            var random = 0
            repeat {
                random = randomNumberInRange(range)
            } while indices.contains(random)
            indices.append(random)
        }

        return indices.map { self[$0] }
    }

【问题讨论】:

  • 使用 arc4random() 代替 srand(seed)
  • arc4random() 无法播种。您确实获得了质量更好的随机数,但如果您的要求每次都要求使用相同的随机数集,那就不合适了。
  • 看起来您必须将代码移动到 C/Obj-C。 Apple 要么没有及时将它移植到 Beta 1,要么非常严厉地强迫你使用“好的”随机数生成器
  • 是的,但你所说的好是什么意思?更高质量的一代?但无论如何,有时播种是必须的。如果苹果不提供种子生成,我会从树上掉下来。
  • 在生成器自我重复之前,可预测性较差,周期较长。请参阅我对愚蠢的 C 包装器的回答

标签: random srand swift3


【解决方案1】:

你可以使用 Swift3 中的 srand48(seed)drand48()

【讨论】:

    【解决方案2】:

    除非您使用 Swift 为非 Apple 平台进行开发,否则您可以在 GameplayKit 中获得更好的随机化 API:多种算法(交易随机性与速度)、可种子化、分发控制等。

    【讨论】:

    • 这将如何转换为支架/rand 替代品?
    【解决方案3】:

    我找不到在 Swift 3 Beta 1 中使用种子随机数的方法。不得不在 C 中编写一个愚蠢的包装函数:

    // ----------------------------------------------
    // my_random.h
    // ----------------------------------------------
    #ifndef my_random_h
    #define my_random_h
    
    #include <stdio.h>
    
    #endif /* my_random_h */
    
    long next_random();
    
    
    // ----------------------------------------------
    // my_random.c
    // ----------------------------------------------
    #include <stdlib.h>
    #include "my_random.h"
    
    long next_random() {
        return random();
    }
    

    您可以使用桥接头将其导入 Swift。然后你可以像这样在 Swift 中调用它:

    srandom(42)
    for _ in 0..<10 {
        let x = next_random()
        print(x)
    }
    

    random 优于 rand。阅读man 页面以讨论这两个功能。


    编辑:

    正如@riskter 所建议的,一种解决方法是使用 GameKit:

    import GameKit
    
    let seed = Data(bytes: [42]) // Use any array of [UInt8]
    let source = GKARC4RandomSource(seed: seed)
    
    for _ in 0..<10 {
        let x = source.nextInt()
        print(x)
    }
    

    【讨论】:

    • 唉,框架中的桥接是不允许的:“:0: error: using bridging headers with framework targets is not supported”。有什么解决方法吗?
    • 按照@rickster 的建议使用 GameKit。查看我编辑的答案
    【解决方案4】:

    对于简单的可重复随机列表,请尝试使用线性同余生成器:

    import Foundation
    
    class LinearCongruntialGenerator
    {
    
        var state = 0 //seed of 0 by default
        let a, c, m, shift: Int
    
        //we will use microsoft random by default
        init() {
            self.a = 214013
            self.c = 2531011
            self.m = Int(pow(2.0, 31.0)) //2^31 or 2147483648
            self.shift = 16
        }
    
        init(a: Int, c: Int, m: Int, shift: Int) {
            self.a = a
            self.c = c
            self.m = m //2^31 or 2147483648
            self.shift = shift
        }
    
        func seed(seed: Int) -> Void {
            state = seed;
        }
    
        func random() -> Int {
            state = (a * state + c) % m
            return state >> shift
        }
    }
    
    let microsoftLinearCongruntialGenerator = LinearCongruntialGenerator()
    
    print("Microsft Rand:")
    
    for i in 0...10
    {
        print(microsoftLinearCongruntialGenerator.random())
    }
    

    更多信息在这里: https://rosettacode.org/wiki/Linear_congruential_generator

    【讨论】:

    • 我喜欢!仅代替缓慢且不准确的 Int(pow(2.0, 31.0)),因为使用双值,我更喜欢 1
    【解决方案5】:

    我只是碰巧将它放在一起用于 Swift 4。我知道 Swift 4.2 有与此不同的新随机扩展,但与 OP 一样,我需要它们在测试期间是可播种的。也许有人会发现它有帮助。如果不播种,它将使用 arc4random,否则将使用 drand48。它避免了两种方式的 mod 偏见。

    import Foundation 
    
    class Random {
    
        static var number = unseededGenerator // the current generator
    
        /**
         * returns a random Int 0..<n
         **/
        func get(anIntLessThan n: Int) -> Int {
            return generatingFunction(n)
        }
    
        class func set(seed: Int) {
            number = seedableGenerator
            srand48(seed)
        }
    
        // Don't normally need to call the rest
    
        typealias GeneratingFunction = (Int) -> Int
    
        static let unseededGenerator = Random(){
            Int(arc4random_uniform(UInt32($0)))
        }
        static let seedableGenerator = Random(){
            Int(drand48() * Double($0))
        }
    
        init(_ gf: @escaping GeneratingFunction) {
            self.generatingFunction = gf
        }
    
        private let generatingFunction: GeneratingFunction
    }
    
    func randomTest() {
        Random.set(seed: 65) // comment this line out for unseeded
        for _ in 0..<10 {
            print(
                Random.number.get(anIntLessThan: 2),
                terminator: " "
            )
        }
    }
    
    
    // Run
    
    randomTest()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-20
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 2017-05-25
      • 1970-01-01
      • 1970-01-01
      • 2016-11-21
      相关资源
      最近更新 更多