使用<random>和一个函数生成随机双精度向量

3

我写了以下函数来生成一个随机的双精度向量:

void getrands(vector<double> *x)
{
  int N=(*x).size();
  uniform_real_distribution<double> unif(0.0,1.0);
  mt19937 re(time(NULL));
  auto generator = bind(unif,re);
  generate_n((*x).begin(),N,generator);
}

在我的main()程序中,如果我尝试在多个向量上调用此函数(比如我想生成10个随机向量),那么我最终得到的所有向量都包含相同的随机数,因为时间不足以获得良好的重新种子。有没有更好的方法来解决这个问题?我应该创建一个随机数组然后再将其转换为向量吗?我习惯于使用matlab,可以只调用X=rand(n,m)...

谢谢!

2个回答

6
更好的方法是使用 std::random_device 来生成随机数。
mt19937 re(std::random_device{}());

你应该将RNG作为参数传递给函数,而不是每次迭代都创建一个新实例。

此外,请注意std::bind会复制其参数,因此在将其作为参数传递给bind之前,您可能需要使用std::ref来封装RNG。


我会重写您的函数如下:

template<typename Generator>
void getrands(vector<double>& x, Generator& gen, unsigned num)
{
  generate_n(std::back_inserter(x), num, std::ref(gen));
}

并将其称为

uniform_real_distribution<double> unif(0.0,1.0);
mt19937 re(std::random_device{}());
auto generator = bind(unif, std::ref(re));

vector<double> vs;

getrands(vs, generator, 10);

这使您能够重复使用相同的随机数生成器,调用函数之前不需要将向量设置为正确的大小,如果需要,还可以向向量添加值。 实时演示

啊,这好像可以运行。我只是在学习c++11,所以这对我来说都是新的。你能详细解释一下并提供关于你上一个有关bind的评论的参考吗? - icurays1
@icurays1 bind函数会复制其参数,所以假设你创建了第二个bind表达式auto generator2 = bind(unif,re);,你会发现调用generatorgenerator2会产生相同的结果序列。如果你将bind表达式改为auto generator = bind(unif, ref(re));,就不会再生成RNG的副本,generatorgenerator2会产生不同的序列。 - Praetorian
好的,那很有道理。我已经尝试将我的RNG re作为变量传递,现在又出现了重复...你觉得这是为什么?感谢你的帮助! - icurays1
@icurays1 函数参数需要是引用类型。例如,void foo(mt19937& rng),而不是 void foo(mt19937 rng)。请查看我发布的链接,其中包含示例用法。 - Praetorian
明白了 - 现在一切都运行良好,再次感谢! - icurays1

1
你应该将 std::mt19937 的定义移到全局作用域,以便它只被初始化一次。随机数生成是一个罕见的情况下全局变量更有意义。此外,种子的最佳实践是使用 std::random_device 而不是当前时间。

我有点困惑应该把它放在哪里——在我的函数外面,作为一个常量...?(我还在适应C++) - icurays1
@icurays1:你知道C/C++中的全局变量概念吗? - Siyuan Ren
原则上是可以的。当我将我的mt19937 re(...)声明移动到函数外部时,我再次获得了重复的行为(即使使用了random_device种子方法)。 - icurays1
@icurays1:很奇怪。你能发一个最小可编译代码来演示你的问题吗? - Siyuan Ren
啊,不用在意了——我不确定为什么它之前不能运行,但现在可以了! - icurays1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接