非常小概率的 C++ 随机数检查
C++ Random Number Check for Very Small Probabilities
我有一个事件发生的概率非常小(顺序为 1e-5),我正在尝试使用统一的随机数来测试是否成功。随着概率下降到 1e-4 左右,成功的比例不再与下面测试代码中的概率匹配。
我怎样才能准确地检查出如此小的成功概率?我尝试使用其他随机数生成器,但我发现的所有建议都是针对我没有使用的 C++11。非常感谢!
#include <cstlib>
#include <iostream>
#include <cmath>
double Prob, rand_num, frac_success;
int num_success, num_tries;
Prob = 1e-4;
num_tries = 1e8;
num_success = 0;
for (int i=0; i<num_tries; i++) {
rand_num = (double) rand() / RAND_MAX; // random number between 0 and 1
if (rand_num < Prob) {
num_success ++; // Record success
}
}
frac_success = double(num_success) / double(num_tries);
cout << Prob << endl << frac_success << endl;
当Prob = 1e-3时,成功的比例大致等于Prob,但对于Prob = 1e-4,它总是大于1.2e-4。这种差异随着概率的降低而变得更糟,并且似乎无法通过增加尝试次数来解决。
编辑:
正如 DiJuMx 和 Stefano Sanfilippo 所说,rand() 似乎根本不是一个足够好的生成器。我决定改用 C++11,这样我就可以使用 uniform_real_distribution 来解决问题(这意味着对其他非 C++11 代码的更改,但令人高兴的是,更改比我预期的要少)。
rand()
是一个非常糟糕的随机数生成器,只用于井字游戏还可以,但不适合任何严肃的业务。
如果你不能使用 C+11 random
模块,你仍然可以利用 Boost.Random
, which works with C++03 too. Browse the generators page 并寻找最合适的。
如果您怀疑您的随机数生成器有偏差,您可以通过 运行 多次检查并生成频率分布来检查它。
尝试使用不同的种子,看看偏差是否成立。
如果它确实有稳定的偏差,记录分布并用它来克服偏差。
听起来您的 RAND_MAX
值太小了。
考虑 rand()
returns 一个介于 0
和 RAND_MAX
之间的整数这一事实。如果你用这个数除以RAND_MAX
,那么除了0
,你能得到的最小数是1.0/RAND_MAX
.
当RAND_MAX
为32767
时,最小值为3e-5
。然而,在我的机器上,RAND_MAX
是 2147483647
,所以最小值是 4e-10
。
或者,查看 Stefano 关于使用 C++ 特定库的回答。
首先,您必须考虑到您的估算器存在一定的误差。没找到好的link,总之就是:
H = success / trials // your estimator
E(H) = p // the expectation value is the real probability
Var(H) = p(1-p)/n // variance of your estimator
仅此一项就表明您应该以较小的概率获得更好的结果。
但是,正如其他答案中所建议的那样,您应该使用适当的随机数生成器。
rng 应该以相同的概率产生每个可能的结果(如果它是统一的)。让我们说一下 RAND_MAX=3
。如果我们 运行 它足够频繁,每个可能的值将以相同的频率出现,我们得到的结果就好像每个值只使用一次一样。现在考虑
for (int i=0;i<4;i++){std::cout << (double)i/3 << std::endl;}
这将产生
0
0.333333
0.666667
1
这将为不太小的概率提供合理的结果(例如,当尝试找到 p=0.5
时,您可以偶然找到确切的值)。然而,当你试图找到一个小概率时,结果会太大。
效果与 RAND_MAX=32767
相同,只是出现的概率较小(在 p < 1/RAND_MAX
左右)。实际上我不知道这是否可以通过简单地除以 RAND_MAX+1
来解决,但是 here 是一个很好地戏剧化并解释了 rand() 问题的视频。
我有一个事件发生的概率非常小(顺序为 1e-5),我正在尝试使用统一的随机数来测试是否成功。随着概率下降到 1e-4 左右,成功的比例不再与下面测试代码中的概率匹配。
我怎样才能准确地检查出如此小的成功概率?我尝试使用其他随机数生成器,但我发现的所有建议都是针对我没有使用的 C++11。非常感谢!
#include <cstlib>
#include <iostream>
#include <cmath>
double Prob, rand_num, frac_success;
int num_success, num_tries;
Prob = 1e-4;
num_tries = 1e8;
num_success = 0;
for (int i=0; i<num_tries; i++) {
rand_num = (double) rand() / RAND_MAX; // random number between 0 and 1
if (rand_num < Prob) {
num_success ++; // Record success
}
}
frac_success = double(num_success) / double(num_tries);
cout << Prob << endl << frac_success << endl;
当Prob = 1e-3时,成功的比例大致等于Prob,但对于Prob = 1e-4,它总是大于1.2e-4。这种差异随着概率的降低而变得更糟,并且似乎无法通过增加尝试次数来解决。
编辑:
正如 DiJuMx 和 Stefano Sanfilippo 所说,rand() 似乎根本不是一个足够好的生成器。我决定改用 C++11,这样我就可以使用 uniform_real_distribution 来解决问题(这意味着对其他非 C++11 代码的更改,但令人高兴的是,更改比我预期的要少)。
rand()
是一个非常糟糕的随机数生成器,只用于井字游戏还可以,但不适合任何严肃的业务。
如果你不能使用 C+11 random
模块,你仍然可以利用 Boost.Random
, which works with C++03 too. Browse the generators page 并寻找最合适的。
如果您怀疑您的随机数生成器有偏差,您可以通过 运行 多次检查并生成频率分布来检查它。
尝试使用不同的种子,看看偏差是否成立。
如果它确实有稳定的偏差,记录分布并用它来克服偏差。
听起来您的 RAND_MAX
值太小了。
考虑 rand()
returns 一个介于 0
和 RAND_MAX
之间的整数这一事实。如果你用这个数除以RAND_MAX
,那么除了0
,你能得到的最小数是1.0/RAND_MAX
.
当RAND_MAX
为32767
时,最小值为3e-5
。然而,在我的机器上,RAND_MAX
是 2147483647
,所以最小值是 4e-10
。
或者,查看 Stefano 关于使用 C++ 特定库的回答。
首先,您必须考虑到您的估算器存在一定的误差。没找到好的link,总之就是:
H = success / trials // your estimator
E(H) = p // the expectation value is the real probability
Var(H) = p(1-p)/n // variance of your estimator
仅此一项就表明您应该以较小的概率获得更好的结果。
但是,正如其他答案中所建议的那样,您应该使用适当的随机数生成器。
rng 应该以相同的概率产生每个可能的结果(如果它是统一的)。让我们说一下 RAND_MAX=3
。如果我们 运行 它足够频繁,每个可能的值将以相同的频率出现,我们得到的结果就好像每个值只使用一次一样。现在考虑
for (int i=0;i<4;i++){std::cout << (double)i/3 << std::endl;}
这将产生
0
0.333333
0.666667
1
这将为不太小的概率提供合理的结果(例如,当尝试找到 p=0.5
时,您可以偶然找到确切的值)。然而,当你试图找到一个小概率时,结果会太大。
效果与 RAND_MAX=32767
相同,只是出现的概率较小(在 p < 1/RAND_MAX
左右)。实际上我不知道这是否可以通过简单地除以 RAND_MAX+1
来解决,但是 here 是一个很好地戏剧化并解释了 rand() 问题的视频。