Swift - 播种 arc4random_uniform?还是另类?

Swift - Seeding arc4random_uniform? Or alternative?

让我首先说明我要完成的工作:

  1. 我需要在一定范围内随机生成一组数字
  2. 我希望这些数字分布均匀
  3. 我需要能够为随机数生成播种,这样,给定一个种子,生成的随机数将始终相同。

在对 drand48()、rand() 和 arc4random() 进行了大量试验后,我目前决定使用 rand() 获取随机数,并使用 srand() 进行播种。这是一个从我正在做的事情中简化而来的小例子:

let seed: UInt32 = 10
srand(seed)
let start = 0
let end = 100
let randomNumber = Double(rand()) % (end + 1 - start) + start

这行得通。给定相同的种子,会产生相同的随机数。执行多个 randomNumber 计算会产生多个不同的随机数。通过 srand 重新播种会重新启动 "randomness"。

唯一的缺点是 rand() 不是均匀分布的。事实上,我几乎总是得到一组在大多数情况下线性增加的数字。

听起来 arc4random_uniform 会生成更多统一的随机输出,但是根据我的研究,不可能对 arc4random 进行播种,因为它会在第一次调用时自行播种,而且不一定"designed" 将在外部播种。

所以我的问题;有没有比 srand() / rand() 更好的替代方法,它仍然会为我提供给定种子的相同输出,但这些输出分布更均匀?

谢谢, - 亚当

我知道 "GameKit" 听起来它只是用于游戏,但它包含一个严肃的随机数生成系统。我建议你看看GKMersenneTwisterRandomSource and GKRandomDistributionGKMersenneTwisterRandomSource 采用随机种子(如果您选择的话),GKRandomDistribution class 实现 均匀分布 。一起使用,它们完全符合您的要求。

import GameKit

// The Mersenne Twister is a very good algorithm for generating random
// numbers, plus you can give it a seed...    
let rs = GKMersenneTwisterRandomSource()
rs.seed = 1780680306855649768

// Use the random source and a lowest and highest value to create a 
// GKRandomDistribution object that will provide the random numbers.   
let rd = GKRandomDistribution(randomSource: rs, lowestValue: 0, highestValue: 100)

// Now generate 10 numbers in the range 0...100:    
for _ in 1...10 {
    print(rd.nextInt())
}

print("---")

// Let's set the seed back to the starting value, and print the same 10
// random numbers.    
rs.seed = 1780680306855649768
for _ in 1...10 {
    print(rd.nextInt())
}

事实证明,srand / rand 组合确实符合我的需要,导致结果不出现的问题 "uniformly distributed" 是我自己逻辑中的一个错误。

供参考,基本上我所做的是这个(实际上它要复杂得多,但出于演示目的):

let start = 0
let end = 100

for x in 0..<10 {

   let seed = UInt32(x)
   srand(seed)
   let randomNumber = Double(rand()) % (end + 1 - start) + start

   // Do something with random number

}

写成上面更简单的形式,问题就很明显了。我在循环的每次迭代中重新播种,种子值只是线性增加。因此,随机结果也是线性递增的。

简单的解决方案是不对每个循环迭代重新播种,而是在循环之前播种一次。例如:

let start = 0
let end = 100
let seed = UInt32(100)
srand(seed)

for x in 0..<10 {

   let randomNumber = Double(rand()) % (end + 1 - start) + start

   // Do something with random number

}

通过这个简单的更改,结果值似乎确实在示例中使用的 0 到 100 范围内均匀分布。我不确定是否有 "more uniform" 方法可以做到这一点,但我认为是有的,因为我读过 arc4random is "far superior" to drand / rand / erand / etc functions for uniform random number generation ,但至少这似乎可以满足我的需要。

如果其他人提出更好的方法来完成我所追求的目标,我将把这个问题悬而未决一段时间。