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 ?
谢谢。
编辑:
最后,我做了两个令人惊讶的实验,第一个是真正的骰子:
令我惊讶的是,我不会说它是 运行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
现在我相信玩家们不会抱怨了……
我一直在使用 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 ?
谢谢。
编辑:
最后,我做了两个令人惊讶的实验,第一个是真正的骰子:
令我惊讶的是,我不会说它是 运行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
现在我相信玩家们不会抱怨了……