我一直在使用 arc4random()和 arc4random_uniform(),我总觉得它们不是完全随机的,例如,我是随机从数组中选择值,但是当我连续多次生成它们时,通常出现的值是相同的,所以今天我认为我会使用Xcode操场来查看这些函数的行为,所以我首先测试 arc4random_uniform 来生成0到4之间的数字,所以我使用了这个算法:
import Cocoa
var number = 0
for i in 1...20 {
number = Int(arc4random_uniform(5))
}
我跑了几次,以下是大多数时候价值观的演变:
因此,您可以看到值重复增加和减少,并且一旦值处于最大值/最小值,它们通常会在特定时间内保持不变(请参阅第5步中的第一个屏幕截图,值保持在在6个步骤中,问题在于它并非完全不寻常,在我的测试中,函数实际上大部分都是以这种方式运行。
现在,如果我们看一下arc4random()
,它基本上是相同的:
所以这是我的问题:
谢谢。
编辑:
最后,我做了两个令人惊讶的实验,第一个带有真正的骰子:
让我感到惊讶的是,我不会说它是随机的,因为我看到了与arc4random()&amp ;;非随机描述的相同类型的模式。 arc4random_uniform(),正如Jean-BaptisteYunès所指出的那样,人类不容易看出一系列数字是否真的是随机的。
我还想做一个更“科学”的实验,所以我做了这个算法:
import Foundation
var appeared = [0,0,0,0,0,0,0,0,0,0,0]
var numberOfGenerations = 1000
for _ in 1...numberOfGenerations {
let randomNumber = Int(arc4random_uniform(11))
appeared[randomNumber]++
}
for (number,numberOfTimes) in enumerate(appeared) {
println("\(number) appeard \(numberOfTimes) times (\(Double(numberOfGenerations)/Double(numberOfTimes))%)")
}
要查看每个号码出现的次数,并且有效地随机生成这些数字,例如,这是控制台的一个输出:
0次出现99次
1次出现了97次
2次出现了78次
3次出现了80次
4次出现了87次
5次出现了107次
6次出现86次
7次出现了97次
8次出现了100次
9次出现91次
10次出现了78次。
所以这绝对没问题
编辑#2:我再次制作了更多卷筒的骰子实验,这对我来说仍然令人惊讶:
答案 0 :(得分:9)
算法无法生成真正的随机数字序列。它们只能产生伪随机数字序列(看似随机序列的东西)。因此,根据所选择的算法,“随机性”的质量可能会有所不同。 arc4random()
序列的质量通常被认为具有良好的随机性。
你无法直观地分析序列的随机性......人类检测随机性非常糟糕!他们倾向于找到一些没有的结构。在你的图表中没有什么真正的伤害(除了6个连续的罕见子序列,但这是随机性,有时会发生不寻常的事情)。如果您使用骰子生成序列并绘制其图形,您会感到惊讶。请注意,只有20个数字的样本无法根据其随机性进行严格分析,您需要更大的样本。
如果您需要其他类型的随机性,您可以尝试使用/dev/random
伪文件,每次读入时都会生成一个随机数。序列由算法和外部物理事件组合生成你的电脑就会发生这种情况。
答案 1 :(得分:4)
这取决于你说随机时的意思。
正如评论中所述,真正的随机性是块状的。预计会有长串重复或接近的值。
如果这不符合您的要求,那么您需要更好地定义您的要求。
其他选项可能包括使用shuffle algorithm来排序数组中的内容,或使用low-discrepancy sequence算法来给出相等的值分布。
答案 2 :(得分:1)
我真的不同意人类很难发现随机性的想法。 投掷6对骰子后获得1-1-2-2-3-3-4-4-5-5-6-6,您是否会满意?但是骰子频率是完美的……
这正是我在arc4random或arc4random_uniform函数中遇到的问题。 多年来,我一直在开发一种西洋双陆棋应用程序,该应用程序基于由词冠军玩家训练的神经网络。我确实知道它的演奏比任何人都好,但是许多用户认为它在作弊。有时我也有疑问,所以我决定自己扔所有骰子……
即使频率还可以,我对arc4random也不满意。 我总是掷几个骰子,结果导致无法接受的情况,例如:为同一位玩家获得五个连续的双骰子,等待12个回合(24个骰子),直到出现前6个。
易于测试(C代码):
void randomDices ( int * dice1, int * dice2, int player )
{
( * dice1 ) = arc4random_uniform ( 6 ) ;
( * dice2 ) = arc4random_uniform ( 6 ) ;
// Add to your statistics
[self didRandomDice1:( * dice1 ) dice2:( * dice2 ) forPlayer:player] ;
}
也许arc4random不希望在短时间内被两次调用...
因此,我尝试了几种解决方案,最后选择了这段代码,该代码在arc4random_uniform之后运行第二级随机化:
int CFRandomDice ()
{
int __result = -1 ;
BOOL __found = NO ;
while ( ! __found )
{
// random int big enough but not too big
int __bigint = arc4random_uniform ( 10000 ) ;
// Searching for the first character between '1' and '6'
// in the string version of bigint :
NSString * __bigString = @( __bigint ).stringValue ;
NSInteger __nbcar = __bigString.length ;
NSInteger __i = 0 ;
while ( ( __i < __nbcar ) && ( ! __found ) )
{
unichar __ch = [__bigString characterAtIndex:__i] ;
if ( ( __ch >= '1' ) && ( __ch <= '6' ) )
{
__found = YES ;
__result = __ch - '1' + 1 ;
}
else
{
__i++ ;
}
}
}
return ( __result ) ;
}
此代码使用arc4random_uniform(10000)创建一个随机数,将其转换为字符串,然后在字符串中搜索“ 1”和“ 6”之间的第一个数字。
在我看来,这是随机分配骰子的一种很好的方法,因为: 1 /频率可以(请参阅下面的统计信息); 2 /异常情况下会出现异常的骰子序列。
10000 dices test:
----------
Game Stats
----------
HIM :
Total 1 = 3297
Total 2 = 3378
Total 3 = 3303
Total 4 = 3365
Total 5 = 3386
Total 6 = 3271
----------
ME :
Total 1 = 3316
Total 2 = 3289
Total 3 = 3282
Total 4 = 3467
Total 5 = 3236
Total 6 = 3410
----------
HIM doubles = 1623
ME doubles = 1648
现在我确定玩家不会抱怨…