arc4random()和arc4random_uniform()不是真的随机吗?

时间:2015-01-07 06:35:16

标签: swift random swift-playground darwin

我一直在使用 arc4random() arc4random_uniform(),我总觉得它们不是完全随机的,例如,我是随机从数组中选择值,但是当我连续多次生成它们时,通常出现的值是相同的,所以今天我认为我会使用Xcode操场来查看这些函数的行为,所以我首先测试 arc4random_uniform 来生成0到4之间的数字,所以我使用了这个算法:

import Cocoa

var number = 0

for i in 1...20 {
    number = Int(arc4random_uniform(5))
}

我跑了几次,以下是大多数时候价值观的演变: enter image description here enter image description here

因此,您可以看到值重复增加和减少,并且一旦值处于最大值/最小值,它们通常会在特定时间内保持不变(请参阅第5步中的第一个屏幕截图,值保持在在6个步骤中,问题在于它并非完全不寻常,在我的测试中,函数实际上大部分都是以这种方式运行。

现在,如果我们看一下arc4random(),它基本上是相同的:
enter image description here enter image description here

所以这是我的问题:

  • 为什么这个功能会以这种方式运行?
  • 如何让它更随意?

谢谢。

编辑:
最后,我做了两个令人惊讶的实验,第一个带有真正的骰子: enter image description here
让我感到惊讶的是,我不会说它是随机的,因为我看到了与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:我再次制作了更多卷筒的骰子实验,这对我来说仍然令人惊讶:
enter image description here

3 个答案:

答案 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

现在我确定玩家不会抱怨…