全局随机数
本节阅读量:如果想在多个函数或文件中使用随机数生成器,会发生什么情况?一种方法是在main()函数中创建(并播种)PRNG,然后把它传递到所有需要它的地方。但这个对象可能只是偶尔使用,却需要在许多地方传来传去。这样的传递会给代码增加不少混乱。
或者,您可以在每个需要它的函数中创建静态局部std::mt19937变量(使用静态变量可以确保它只被播种一次)。然而,让每个使用随机数生成器的函数都定义并播种自己的本地生成器是多余的,而且每个生成器的调用次数较少,也可能导致结果质量较低。
我们真正想要的是一个单一的PRNG对象,它可以跨越所有函数和文件,在任何地方共享和访问。这里的最佳选择是创建全局随机数生成器对象。还记得我们曾建议避免非常量全局变量吗?这是一个例外。
下面是一个简单的、仅含头文件的解决方案,您可以将其包含在任何使用全局随机数的文件中:
random.h:
|
|
以及如何使用它的示例程序:
主.cpp:
|
|
通常,当头文件被包含到多个源文件中时,在头文件中定义变量和函数会导致违反单定义规则(ODR)。然而,这里已经将mt变量和相关函数声明为inline,只要这些重复定义完全相同,就不会违反ODR。因为我们使用#include引入头文件(而不是手动键入或复制/粘贴这些定义),所以可以确保它们是相同的。
我们必须克服的另一个挑战是如何初始化全局Random::mt对象,因为我们希望它能自动播种,这样就不必记住显式调用初始化函数。初始值设定项必须是表达式。为了初始化std::mt19937,需要几个辅助对象(std::random_device和std::seed_seq),而这些对象通常需要通过语句来定义。这就是辅助函数派上用场的地方。函数调用是表达式,因此可以使用函数的返回值作为初始值设定项。在函数中,可以包含所需的任意语句组合。因此,generate()函数会创建并返回一个完全播种的std::mt19937对象(使用系统时钟和std::random_device播种),用作全局Random::mt对象的初始值设定项。
一旦引用“Random.h”,就可以用以下两种方式之一使用它:
- 可以调用Random::get()在两个值之间生成一个随机数(包含两个端点)。
- 可以通过Random::mt直接访问std::mt19937对象,并对其执行任何操作。