【发布时间】:2013-01-18 16:38:09
【问题描述】:
考虑一个算法来测试在特定次数的尝试后从一组 N 个唯一数字中选出某个数字的概率(例如,当 N=2 时,轮盘赌(不为 0)中的概率是多少? X 试图让黑方获胜?)。
这个的正确分布是 pow(1-1/N,X-1)*(1/N)。
但是,当我使用以下代码对此进行测试时,在 X=31 处总是有一个深沟,与 N 无关,与种子无关。
这是一个由于使用中的 PRNG 的实现细节而无法避免的内在缺陷,这是一个真正的错误,还是我忽略了一些明显的东西?
// C
#include <sys/times.h>
#include <math.h>
#include <stdio.h>
int array[101];
void main(){
int nsamples=10000000;
double breakVal,diffVal;
int i,cnt;
// seed, but doesn't change anything
struct tms time;
srandom(times(&time));
// sample
for(i=0;i<nsamples;i++){
cnt=1;
do{
if((random()%36)==0) // break if 0 is chosen
break;
cnt++;
}while(cnt<100);
array[cnt]++;
}
// show distribution
for(i=1;i<100;i++){
breakVal=array[i]/(double)nsamples; // normalize
diffVal=breakVal-pow(1-1/36.,i-1)*1/36.; // difference to expected value
printf("%d %.12g %.12g\n",i,breakVal,diffVal);
}
}
在带有 libc6 包 2.15-0ubuntu20 和 Intel Core i5-2500 SandyBridge 的最新 Xubuntu 12.10 上进行了测试,但几年前我已经在一台较旧的 Ubuntu 机器上发现了这一点。
我还使用 Unity3D/Mono 在 Windows 7 上对此进行了测试(但不确定是哪个 Mono 版本),这里使用 System.Random 时,沟渠发生在 X=55,而 Unity 的内置 Unity.Random 没有可见沟渠(至少对于 X
分布:
区别:
【问题讨论】:
-
我认为没有人声称 glibc 中的随机函数特别“高质量”。如果您想要更好的东西,请使用 Mersenne Twister 或其他一些“专业级”RNG。 C 库 [和其他类似库] 提供的那个往往是为了简单而不是“完美”而编写的。
-
1) main 应该返回 int 2) 模 36 是可疑的,我建议你先尝试模 32,或者另一个 2 的幂。
-
我很确定 pgp/gpg [或任何其他不是由“奶酪”制成的加密机制] 不使用 libc 的算法,尽管我不得不承认我不了解这些特定工具的用途。
-
除非你是developing for Plan 9,否则最好改掉写
void main的习惯。 -
rand % N有缺陷。我的建议是先使用适当的基于拒绝的方法,然后重新评估。
标签: c algorithm math random glibc