为蒙特卡罗模拟种子mt19937_64的最佳方法

Mat*_*200 30 c++ random mersenne-twister c++11 random-seed

我正在研究一个运行蒙特卡罗模拟的程序; 具体来说,我正在使用Metropolis算法.该程序需要产生数十亿的"随机"数字.我知道梅森捻线机在蒙特卡罗模拟中非常受欢迎,但我想确保以最佳方式播种发电机.

目前我正在使用以下方法计算32位种子:

mt19937_64 prng; //pseudo random number generator
unsigned long seed; //store seed so that every run can follow the same sequence
unsigned char seed_count; //to help keep seeds from repeating because of temporal proximity

unsigned long genSeed() {
    return (  static_cast<unsigned long>(time(NULL))      << 16 )
         | ( (static_cast<unsigned long>(clock()) & 0xFF) << 8  )
         | ( (static_cast<unsigned long>(seed_count++) & 0xFF) );
}

//...

seed = genSeed();
prng.seed(seed);
Run Code Online (Sandbox Code Playgroud)

我有一种感觉,有更好的方法来确保不重复的新种子,我很确定mt19937_64可以播种超过32位.有没有人有什么建议?

Pra*_*ian 25

使用std::random_device产生的种子.它将提供非确定性随机数,只要您的实现支持它.否则允许使用其他随机数引擎.

std::mt19937_64 prng;
seed = std::random_device{}();
prng.seed(seed);
Run Code Online (Sandbox Code Playgroud)

operator()std::random_device返回unsigned int,所以如果你的平台有32位intS,和你想有一个64位的种子,你需要两次调用它.

std::mt19937_64 prng;
std::random_device device;
seed = (static_cast<uint64_t>(device()) << 32) | device();
prng.seed(seed);
Run Code Online (Sandbox Code Playgroud)

另一个可用选项是std::seed_seq用于播种PRNG.这允许PRNG调用seed_seq::generate,其在[0≤i<2 32 ]的范围内产生非偏置序列,其输出范围足够大以填充其整个状态.

std::mt19937_64 prng;
std::random_device device;
std::seed_seq seq{device(), device(), device(), device()};
prng.seed(seq);
Run Code Online (Sandbox Code Playgroud)

我正在调用random_device4次来创建一个4元素的初始序列seed_seq.但是,就初始序列中元素的长度或来源而言,我不确定最佳做法是什么.


Dar*_*ioP 5

让我们回顾一下(也是注释),我们想生成不同的种子,以在以下每种情况下获得独立的随机数序列:

  1. 该程序稍后在同一台计算机上重新启动,
  2. 在同一台计算机上同时启动两个线程,
  3. 该程序同时在两台不同的计算机上启动。

1是从纪元以来使用时间来解决的,2是通过全局原子计数器来解决的,3是通过平台相关的id来解决的(请参阅如何以跨平台的方式获取(几乎)唯一的系统标识符?

现在的重点是,将它们组合成uint_fast64_t的种子类型的最佳方法是什么std::mt19937_64?我在这里假设我们不知道每个参数的范围是先验的,或者它们太大,以至于我们不能只是简单地通过移位来获得唯一的种子。

A std::seed_seq是最简单的方法,但是它的返回类型uint_least32_t不是我们的最佳选择。

一个好的64位哈希器是一个更好的选择。STL std::hashfunctional标题下提供了一种可能性,可以将上面的三个数字连接成一个字符串,然后将其传递给哈希器。返回类型是size_t在64台机器上很可能符合我们要求的返回类型。

冲突不太可能发生,但当然有可能发生,如果您要确保不要建立包含一个以上序列的统计信息,则只能存储种子并丢弃重复的运行。

std::random_device也可以使用A 生成种子(冲突可能仍然会发生,很难说是多多少少),但是由于实现依赖于库,并且可能会归结为伪随机生成器,因此必须检查A的熵避免使用零熵设备,因为您可能会破坏上面的几点(尤其是第3点)。不幸的是,只有将程序带到特定计算机上并使用已安装的库进行测试时,才能发现熵。