python和c++中类似的随机数生成但得到不同的输出
我有两个函数,在 c++ 和 python 中,它们确定具有一定概率的事件在多次滚动中发生的次数。
蟒蛇版本:
def get_loot(rolls):
drops = 0
for i in range(rolls):
# getting a random float with 2 decimal places
roll = random.randint(0, 10000) / 100
if roll < 0.04:
drops += 1
return drops
for i in range(0, 10):
print(get_loot(1000000))
蟒蛇输出:
371
396
392
406
384
392
380
411
393
434
C++ 版本:
int get_drops(int rolls){
int drops = 0;
for(int i = 0; i < rolls; i++){
// getting a random float with 2 decimal places
float roll = (rand() % 10000)/100.0f;
if (roll < 0.04){
drops++;
}
}
return drops;
}
int main()
{
srand(time(NULL));
for (int i = 0; i <= 10; i++){
cout << get_drops(1000000) << "n";
}
}
C++ 输出:
602
626
579
589
567
620
603
608
594
610
626
cood 看起来完全一样(至少对我来说)。这两个函数模拟一个事件的发生,概率为 0.04,超过 1,000,000 次滚动。但是python版本的输出比c++版本低30%左右。这两个版本有什么不同,为什么它们有不同的输出?
回答
在 C++ rand() 中“返回一个介于 0 和 RAND_MAX 之间的伪随机整数。”
RAND_MAX 是“依赖于库,但保证在任何标准库实现上至少为 32767。”
让我们设置RAND_MAX为 32,767。
在计算 [0, 32767) % 10000 时,随机数生成是有偏差的。
值 0-2,767 在 (% 10000)-> 范围内都出现了 4 次
| 价值 | 计算 | 结果 |
|---|---|---|
| 1 | 1% 10000 | 1 |
| 10001 | 10001 % 10000 | 1 |
| 20001 | 20001 % 10000 | 1 |
| 30001 | 30001 % 10000 | 1 |
其中值 2,768-9,999 在范围内仅出现 3 次 (% 10000) ->
| 价值 | 计算 | 结果 |
|---|---|---|
| 2768 | 2768 % 10000 | 2768 |
| 12768 | 12768 % 10000 | 2768 |
| 22768 | 22768 % 10000 | 2768 |
这使得值 0-2767 比值 2768-9,999 出现的可能性高 25%(假设rand()实际上在 0 和 RAND_MAX 之间产生均匀分布)。
另一方面,Python 使用randint会在开始和结束之间产生均匀分布,就像randint“randrange(a, b+1) 的别名”一样
而randrange(在Python 3.2和更高版本)会产生均匀分布值:
在 3.2 版更改: randrange() 在生成均匀分布的值方面更加复杂。以前它使用像 int(random()*n) 这样的样式,这可能会产生稍微不均匀的分布。
在 C++ 中有几种生成随机数的方法。也许最相似的python是使用 Mersenne Twister 引擎(如果有一些差异,它与 python 相同)。
通过uniform_int_distribution使用mt19937:
#include <iostream>
#include <random>
#include <chrono>
int get_drops(int rolls) {
std::mt19937 e{
static_cast<unsigned int> (
std::chrono::steady_clock::now().time_since_epoch().count()
)
};
std::uniform_int_distribution<int> d{0, 9999};
int drops = 0;
for (int i = 0; i < rolls; i++) {
float roll = d(e) / 100.0f;
if (roll < 0.04) {
drops++;
}
}
return drops;
}
int main() {
for (int i = 0; i <= 10; i++) {
std::cout << get_drops(1000000) << "n";
}
}
值得注意的是,这两个引擎的底层实现以及播种和分发都略有不同,但是,这将更接近python。
或者,正如马蒂亚斯·弗里普( Matthias Fripp)建议扩大兰特并除以RAND_MAX:
int get_drops(int rolls) {
int drops = 0;
for (int i = 0; i < rolls; i++) {
float roll = (10000 * rand() / RAND_MAX) / 100.0f;
if (roll < 0.04) {
drops++;
}
}
return drops;
}
这也更接近 python 输出(同样在底层实现中生成随机数的方式存在一些差异)。
- 可能值得指出的是,在 C 中获取 0 到 10000 之间的随机数的正确方法是使用“10000 * rand()/RAND_MAX”。