【问题标题】:generating random numbers in C++ using TR1 /dev/random (resilient to <1 second runs)使用 TR1 /dev/random 在 C++ 中生成随机数(可恢复到 <1 秒的运行)
【发布时间】:2012-01-29 11:31:26
【问题描述】:

我想以不使用标准 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; 
} 

【问题讨论】:

  • 您愿意使用操作系统/编译器特定的功能吗?
  • 我想避免这种情况......我将在各种系统上部署它
  • 多种多样?它们仍然都是 x86 吗?
  • 好的,然后在此处查看我的答案:stackoverflow.com/questions/7617587/… 如果您愿意,我也可以将其作为答案发布在这里。
  • 作为一个特定于平台的技巧,它对代码可移植性的影响很小,如果您愿意假设目标系统具有堆栈地址随机化(现在在桌面操作系统上很常见),您只需将uint_ptr_t 的局部变量地址并将其用作种子。哦,如果你对它不是 100% 有信心,你可以用你找到的任何其他方法对它进行异或运算,种子会和最好的一样好。

标签: c++ random random-seed


【解决方案1】:

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

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

几周前我在这里问了一个关于播种的问题,并获得了指向以下文章的链接,您可能会发现它也很有用。 Good Practice in (Pseudo) Random Number Generation for Bioinformatics Applications 请参阅有关播种或预热生成器的部分。

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

还可以考虑将Multiply with carry 作为替代 PRNG 算法。

【讨论】:

    【解决方案2】:

    应 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 的极好种子。

    【讨论】:

      【解决方案3】:

      [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 中,您必须将种子参数包装到另一个类中。由于调用参数在两个库中都有效,所以我会这样做是为了可维护性,除非您有更苛刻的需求。

      【讨论】:

      • 请注意,不能保证您从random_device() 构造函数中获得哪个 随机设备。它可能是/dev/random/dev/urandomrdtsc-based、en.wikipedia.org/wiki/RdRand,甚至是另一个伪 rng。实现可以为构造函数定义一个字符串参数,在选项之间进行选择,但默认值应该是不错的。
      • random_device 是一个函数对象,所以eng(dev_random())
      • @Cubbi:不:引擎既有一个采用整数的构造函数,也有一个采用函数对象的构造函数:“X(g) — 创建一个引擎,其初始内部状态由连续的结果给出g 的调用。” --[tr.rand.req] 表 16。这允许他们填充整个状态,而不仅仅是它的第一个单词。 (当然,任何给定的实现都可能没有实现整个规范。)
      • 啊,确实。 TR1 与 C++11 的另一个区别。
      • @Cubbi:哦。我没有注意到这已经改变了。我将更改我的答案以建议调用 random_device 的理论,即您很少需要超过 UINT_MAX 可能的不同序列,无论如何都提供基于 time() 的代码,并在 TR1 和 C++ 之间保持相同的源代码11不错。谢谢!
      猜你喜欢
      • 2011-03-11
      • 1970-01-01
      • 2012-05-20
      • 2016-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      相关资源
      最近更新 更多