6

我想以不使用标准rand()srand(time(NULL))方法的方式在 C++ 中生成 0 到 1 之间的统一随机数。这样做的原因是,如果我在时钟的同一秒内多次运行应用程序,种子将完全相同并产生相同的输出。

我不想依赖于 boost 或操作系统/编译器的细节。可以假设为 x86。

似乎另一种方法是使用 TR1(我没有 C++11)并/dev/random以某种方式播种?

现在我有这个,但它仍然time(NULL)用作种子,在 1 秒内无法正常运行:

#include <iostream> 
#include <tr1/random> 

int main() 
{ 
  std::tr1::mt19937 eng; 
  eng.seed(time(NULL)); 
  std::tr1::uniform_int<int> unif(1, RAND_MAX); 
  int u = unif(eng); 
  std::cout << (float)u/RAND_MAX << std::endl; 
} 
4

3 回答 3

7

应OP的要求发布:

这仍然是特定于编译器的,但仍然适用于几乎所有面向 x86 的编译器:

#ifdef _WIN32

//  Windows
#define rdtsc  __rdtsc

#else

//  For everything else
unsigned long long rdtsc(){
    unsigned int lo,hi;
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    return ((unsigned long long)hi << 32) | lo;
}

#endif

int main() 
{ 
  std::tr1::mt19937 eng; 
  eng.seed( rdtsc() );    //  Seed with rdtsc.
  std::tr1::uniform_int<int> unif(1, RAND_MAX); 
  int u = unif(eng); 
  std::cout << (float)u/RAND_MAX << std::endl; 
} 

这里的想法是用rdtsc循环计数器播种随机数生成器。

这样做的原因是因为rdtsc循环计数器以与 CPU 频率大致(通常相同)的速度进行迭代。因此,两次调用它返回相同值的可能性非常小——因此,它成为 RNG 的极好种子。

于 2011-12-28T21:13:14.613 回答
4

[tr.rand.device] 中的 TR1 指定了一个random_device从依赖于实现的源生成无符号整数的类。所以以下应该可以工作,虽然我自己没有编译它:

int main() {
  std::tr1::random_device dev_random;
  std::tr1::mt19937 eng(dev_random());
  ...

在 TR1 中,直接传递 dev_random 而不调用它可以更随机地初始化 eng 的状态,但在 C++11 中,您必须将种子参数包装到另一个类中。由于调用参数在两个库中都有效,因此我会这样做是为了可维护性,除非您有更苛刻的需求。

于 2011-12-28T21:30:15.997 回答
1

您的问题与您播种随机数生成器的方式有关。显然,使用 time(NULL) 播种将在播种后的那一秒内产生相同的 PRNG 序列。这是播种 rand 的最常见方法,但不幸的是,由于这个问题,这是一种不好的做法。不仅如此,我还读到它会导致结果出现偏差。

请注意,如果使用相同的值作为种子,每个 PRNG 都会产生相同的结果。因此,您的问题与生成器无关,更多与播种有关。

几周前,我在这里问了一个关于播种的问题,并获得了指向以下文章的链接,您可能也会发现它很有用。 用于生物信息学应用的(伪)随机数生成的良好实践 请参阅有关播种或预热发生器的部分。

rand() 不是最好的随机数生成器,但在许多情况下都适用,只要它被正确播种。如果您想要重复序列非常大的更好的东西,那么该链接中提供了一些。或者使用基于 TR1 的。就个人而言,我会使用更可移植的基于 C++03 的代码并避开 TR1。

还可以考虑乘以进位作为替代 PRNG 算法。

于 2011-12-28T21:10:09.157 回答