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

arc4random() and arc4random_uniform() not really random?

我一直在使用 arc4运行dom()arc4random_uniform() 并且我一直都有感觉它们不完全是 运行dom,例如,我是 运行domly 从数组中选择值,但通常出现的值是当我连续多次生成它们时也是如此,所以今天我想我会使用 Xcode playground 来查看这些函数的行为,所以我首先测试 arc4random_uniform 生成 0 到 4 之间的数字,所以我使用了这个算法:

import Cocoa

var number = 0

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

我 运行 好几次了,下面是大多数时候价值观是如何演变的:

所以你可以看到数值在反复增加和减少,一旦数值达到 maximum/minimum,它们通常会在一段时间内保持不变(参见第 5 步的第一个屏幕截图,在 6 个步骤中该值保持在 3,问题是它一点也不异常,在我的测试中,该函数实际上大部分时间都以这种方式运行。

现在,如果我们查看 arc4random(),基本相同:

所以这是我的问题:

谢谢。

编辑:
最后,我做了两个令人惊讶的实验,第一个是真正的骰子:

令我惊讶的是,我不会说它是 运行dom,因为我看到的模式与 arc4non-random 描述的模式相同运行dom() & arc4random_uniform(),所以正如 Jean-Baptiste Yunès 指出的那样,人类并不善于判断一个数字序列是否真的 运行dom.

我也想做更多的"scientific"实验,所以我做了这个算法:

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))%)")
}

查看每个数字出现了多少次,并且这些数字实际上是 运行domly 生成的,例如,这是控制台的一个输出:
0出现了99次。
1出现了97次。
2出现了78次。
3出现了80次。
4出现了87次
5出现了107次。
6出现了86次。
7出现了97次。
8出现100次
9出现了91次。
10出现了78次。

所以肯定可以

编辑#2:我再次进行了更多掷骰子实验,它仍然让我感到惊讶:

算法无法生成真正的随机数字序列。它们只能产生伪随机数字序列(看起来像随机序列的东西)。因此,根据选择的算法,"randomness" 的质量可能会有所不同。 arc4random()序列的质量一般被认为具有很好的随机性。

您无法直观地分析序列的随机性...人类检测随机性的能力非常差!他们倾向于在没有的地方找到一些结构。在你的图表中没有什么真正的伤害(除了罕见的连续 6 个 3 的子序列,但这是随机的,有时会发生不寻常的事情)。如果您使用骰子生成序列并绘制其图形,您会感到惊讶。请注意,只有 20 个数字的样本无法针对其随机性进行认真分析,您需要更大的样本。

如果你需要一些其他的随机性,你可以尝试使用/dev/random伪文件,它会在你每次读入时生成一个随机数。序列是由算法和外部混合生成的您计算机中发生的物理事件。

这取决于你所说的随机是什么意思。

如评论中所述,真正的随机性是块状的。需要长字符串重复或接近值。

如果这不符合您的要求,那么您需要更好地定义您的要求。

其他选项可能包括使用 shuffle algorithm to dis-order things in an array, or use an low-discrepancy sequence 算法来平均分配值。

我不太同意人类检测随机性非常差的想法。 如果掷 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

现在我相信玩家们不会抱怨了……