【c++】如何均匀生成随机数

从一个具体的例子说起:如何均匀生成 1 亿以内的随机数? 所谓“均匀”,意味着生成概率相等。

从 rand() 函数开始

生成随机数,第一反应是使用 rand() 函数。rand() 函数是 C 语言中用来生成随机数的函数:

#include <stdlib.h>

void srand(unsigned int seed);
int rand(void);
int rand_r(unsigned int *seedp);

rand() 函数可以生成 [0, RAND_MAX] 之间的数字,其中 RAND_MAX 一般是 2147483647。

传统的 rand() 函数在使用前需要使用 srand() 函数设置随机种子。由于 rand() 函数内部使用了静态变量保存状态,调用 rand() 函数时会进行加锁,并且是不可重入的。为了解决这个问题,可以使用 rand_r() 函数,它是 rand() 的可重入版本,使用参数 seedp 来保存相应的状态。

为了生成 1 亿以内的随机数,最简单的方式是使用取模运算:rand() % 100000000。但是这种方法并不是均匀的,因为对于 [0, 99999999] 这 1 亿个数字来说,概率是不相等的。例如,随机生成数字 0 的情况有 22 种可能,而随机生成数字 99999999 的情况只有 21 种可能。为了得到均匀分布的随机数,需要使用其他方法来处理。

C++ 的 uniform_int_distribution

C++ 的 uniform_int_distribution 是 C++ 标准库中的一个随机数分布类,用于生成均匀分布的整数随机数。它可以用于生成指定范围内的整数随机数,确保生成的随机数在指定范围内是均匀分布的。

uniform_int_distribution 类通常与 C++ 标准库中的随机数引擎配合使用,例如 std::default_random_engine。通过使用 uniform_int_distribution,可以避免使用取模运算来生成随机数,从而更加方便地生成均匀分布的整数随机数。

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int32_t> distrib(0, 99999999);
    for (uint32_t i = 0; i < 10; i++)
    {
        std::cout << distrib(gen) << std::endl;
    }
}

关于 std::random_device 和 std::mt19937

std::random_device 是 C++ 标准库中用于生成真正随机数的设备。它通常用作生成随机数种子,以便初始化伪随机数生成器。由于 std::random_device 生成的随机数是基于真正的随机事件(例如硬件噪声)而不是伪随机算法,因此它通常用于初始化伪随机数生成器的种子,以提供更好的随机性。

std::mt19937 是 C++ 标准库中的 Mersenne Twister 伪随机数生成器。它是一个高质量的伪随机数生成器,能够生成均匀分布的整数随机数。通常,std::random_device 用于生成种子,然后将该种子传递给 std::mt19937,以初始化 Mersenne Twister 伪随机数生成器。

综合使用 std::random_device 和 std::mt19937 可以提供更好的随机性,并且是 C++ 中生成随机数的常用做法。

当使用 std::random_device 和 std::mt19937 时,可以通过以下代码示例来生成均匀分布的整数随机数:

#include <iostream>
#include <random>

int main() {
    // 使用 random_device 生成种子
    std::random_device rd;
    // 使用 mt19937 作为伪随机数生成器
    std::mt19937 gen(rd());

    // 定义均匀分布的整数随机数分布
    std::uniform_int_distribution<int> dis(1, 100); // 生成 1 到 100 之间的均匀分布的整数随机数

    // 生成随机数
    for (int i = 0; i < 10; ++i) {
        int random_num = dis(gen); // 使用 mt19937 生成随机数
        std::cout << random_num << " ";
    }

    return 0;
}

总的来说,无论是性能还是随机数的质量,std::mt19937 / std::mt19937_64 都是其中出类拔萃的伪随机数生成器。

小结:

1.使用 rand() 取模的方式造成的随机数不均匀概率不算特别大,但具体影响因应用而异。建议尽量避免使用这种方式来生成随机数。
2.使用 time(nullptr) 作为 rand() 的随机种子虽然方便,但存在一些问题:种子变化频率低且不够随机,容易被预测。
3.内部会加锁的 rand() 可以使用 rand_r() 避免,但总体来说,建议不要在 C++ 代码中使用 rand() 系列的函数。
4.使用 std::random_device 生成随机种子,std::mt19937 / std::mt19937_64 生成随机数,以及 std::uniform_int_distribution 生成指定范围的随机数是一个方便又安全的随机数生成组合。

参考资料

1.漫谈随机数

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>