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”。

以上是python和c++中类似的随机数生成但得到不同的输出的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>