Swift - 播种arc4random_uniform?还是另类?

时间:2021-01-25 13:28:25

Let me start by stating what I'm trying to accomplish:

首先让我说明我想要实现的目标:

  1. I need to randomly generate a set of numbers within a range
  2. 我需要在一个范围内随机生成一组数字
  3. I would like those numbers to be somewhat uniformly distributed
  4. 我希望这些数字在某种程度上均匀分布
  5. I need to be able to seed the random number generation such that, given a seed, the resulting random numbers will always be the same.
  6. 我需要能够为随机数生成种子,以便在给定种子的情况下,得到的随机数将始终相同。

After experimenting quite a bit with drand48(), rand() and arc4random(), I have currently settled on using rand() for obtaining a random number, and srand() for seeding. Here is a small example simplified from what I am doing:

在使用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

This works. Given the same seed, the same random number comes out. Performing multiple randomNumber calculations results in multiple, different random numbers coming out. Re-seeding via srand starts the "randomness" over again.

这很有效。给定相同的种子,出现相同的随机数。执行多个randomNumber计算会产生多个不同的随机数。通过srand重新播种再次启动“随机性”。

The only downside is rand() is not uniformly distributed. Indeed I pretty much always end up with a set of numbers that are linearly increasing for the most part.

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

It sounds like arc4random_uniform will generate more of a uniform random output, however from my research it isn't possible to seed arc4random, as it seeds itself the first time it is invoked and isn't necessarily "designed" to be seeded externally.

听起来像arc4random_uniform会产生更多的均匀随机输出,但是根据我的研究,不可能对arc4random进行种子处理,因为它在第一次调用时会自行播种,并且不一定“设计”为外部播种。

So my question; is there a better alternative to srand() / rand() that will still give me the same outputs for a given seed, but those outputs are more uniformly distributed?

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

Thanks, - Adam

谢谢, - 亚当

2 个解决方案

#1


12  

I know "GameKit" sounds like it's just for games, but it contains a serious random number generation system. I suggest you take a look at GKMersenneTwisterRandomSource and GKRandomDistribution. The GKMersenneTwisterRandomSource takes a random seed (if you so choose) and the GKRandomDistribution class implements a Uniform Distribution. Used together, they do exactly what you're looking for.

我知道“GameKit”听起来只是为了游戏,但它包含一个严重的随机数生成系统。我建议你看看GKMersenneTwisterRandomSource和GKRandomDistribution。 GKMersenneTwisterRandomSource采用随机种子(如果您愿意),GKRandomDistribution类实现统一分布。一起使用,它们完全符合您的要求。

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())
}

#2


2  

It turns out, srand / rand combined do suit my needs, the issue causing the results to not appear "uniformly distributed" was a bug in my own logic.

事实证明,srand / rand组合确实符合我的需要,导致结果不会出现“均匀分布”的问题是我自己逻辑中的错误。

For reference, essentially what I was doing was this (in reality it was much more complex, but for demonstration purposes):

作为参考,基本上我正在做的是这个(实际上它更复杂,但出于演示目的):

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

}

Written in the much more simpler form above, the problem becomes obvious. I was re-seeding every iteration of the loop, and the seed value was just incrementing linearly. Because of this, the random results were also incrementing linearly.

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

The simple solution is to not re-seed each loop iteration, but to instead seed once before the loop. For example:

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

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

}

With this simple change, the resulting values do appear to be somewhat uniformly distributed across the 0 to 100 range used in the example. I can't be sure if there is a "more uniform" way of doing this, but I assume there is since I have read arc4random is "far superior" to drand / rand / erand / etc functions for uniform random number generation, but at least this seems to be working for my needs.

通过这种简单的更改,结果值似乎在示例中使用的0到100范围内有些均匀分布。我无法确定是否存在“更均匀”的方式,但我认为自从我读过arc4random对drand / rand / erand / etc函数“远远优于”统一随机数生成,但是至少这似乎符合我的需要。

I will leave this question open for a while longer in the event that someone else comes up with a better approach to accomplishing what I am after.

如果其他人提出更好的方法来完成我所追求的目标,我会暂时搁置这个问题。

#1


12  

I know "GameKit" sounds like it's just for games, but it contains a serious random number generation system. I suggest you take a look at GKMersenneTwisterRandomSource and GKRandomDistribution. The GKMersenneTwisterRandomSource takes a random seed (if you so choose) and the GKRandomDistribution class implements a Uniform Distribution. Used together, they do exactly what you're looking for.

我知道“GameKit”听起来只是为了游戏,但它包含一个严重的随机数生成系统。我建议你看看GKMersenneTwisterRandomSource和GKRandomDistribution。 GKMersenneTwisterRandomSource采用随机种子(如果您愿意),GKRandomDistribution类实现统一分布。一起使用,它们完全符合您的要求。

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())
}

#2


2  

It turns out, srand / rand combined do suit my needs, the issue causing the results to not appear "uniformly distributed" was a bug in my own logic.

事实证明,srand / rand组合确实符合我的需要,导致结果不会出现“均匀分布”的问题是我自己逻辑中的错误。

For reference, essentially what I was doing was this (in reality it was much more complex, but for demonstration purposes):

作为参考,基本上我正在做的是这个(实际上它更复杂,但出于演示目的):

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

}

Written in the much more simpler form above, the problem becomes obvious. I was re-seeding every iteration of the loop, and the seed value was just incrementing linearly. Because of this, the random results were also incrementing linearly.

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

The simple solution is to not re-seed each loop iteration, but to instead seed once before the loop. For example:

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

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

}

With this simple change, the resulting values do appear to be somewhat uniformly distributed across the 0 to 100 range used in the example. I can't be sure if there is a "more uniform" way of doing this, but I assume there is since I have read arc4random is "far superior" to drand / rand / erand / etc functions for uniform random number generation, but at least this seems to be working for my needs.

通过这种简单的更改,结果值似乎在示例中使用的0到100范围内有些均匀分布。我无法确定是否存在“更均匀”的方式,但我认为自从我读过arc4random对drand / rand / erand / etc函数“远远优于”统一随机数生成,但是至少这似乎符合我的需要。

I will leave this question open for a while longer in the event that someone else comes up with a better approach to accomplishing what I am after.

如果其他人提出更好的方法来完成我所追求的目标,我会暂时搁置这个问题。