如何在不使用C++11的情况下使用多个值初始化boost::mt19937?

6

我可以使用:

boost::mt19937 gen(43);   

这个可以正常工作,但是如果我想在使用随机数生成器之前有超过32位的种子,该怎么办?有没有简单的方法将64位或128位种子放入Mersenne Twister中?
我找到了一些加载多个值以生成结果的示例,但是没有一段代码能够正常运行。
这段代码存在一些问题:
std::vector<unsigned int> seedv(1000, 11);
std::vector<unsigned int>::iterator i=seedv.begin();
boost::mt19937 gen2(i, seedv.end());

首先,调用gen2()总是返回相同的值。我不知道我是怎么弄糊涂的。

其次,我不想要1000个种子,但是当我将它降低到600时,它会“抛出一个std::invalid_argument实例,并提示seed调用中没有足够的元素”

这种方法可以缩短为少数几个种子吗?

这里是另一个看起来很简单的代码示例:

std::string seedv("thisistheseed");
std::seed_seq q(seedv.begin(),seedv.end());
boost::mt19937 gen2(q);

但是它无法编译。我最终发现std::seed_seq仅适用于c++11。在我所依赖的库稳定之前,我被困在gcc 4.7中。
我想我只能使用32位种子,但我想要更多一点。
我确实阅读了这篇文章:Boost Mersenne Twister: how to seed with more than one value? 我喜欢从整个向量初始化的想法:
mersenne_twister(seed1) ^ mersenne_twister(seed2)

但我没有看到在不修改Mersenne_Twister.hpp文件的情况下进行操作的方法。

有什么建议吗?

更新:还有一种方法不能实现!

unsigned long seedv[4];
seedv[0]=1;
seedv[1]=2;
seedv[2]=3;
seedv[3]=4;
boost::mt19937 gen2(seedv,4);

如果使用正确的类型转换,这段代码应该可以工作,但是我尝试过的每一种类型转换都无法通过编译器。在C语言中,我可以进行任何类型的转换,但是在C++中有时候仍然会让我感到困惑...

4个回答

5
使用boost::seed_seq代替std::seed_seq更新:使用。
boost::random_device rd;
boost::mt19937 eng(rd);

boost::mt19937允许您使用单个32位值(基础boost::mersenne_twister_enginew参数)或624个值序列(基础模板的n参数)来初始化它的状态。624是mt19937内部状态中的元素数量。

如果您阅读文档,就会发现这两种机制以不同的方式设置引擎的状态。

  • 使用单个值进行初始化会使用该单个值的复杂函数设置引擎状态的每个元素。
  • 使用624个值序列进行初始化会将引擎状态的每个元素设置为相应的种子值。

重点是,boost::mt19937本身不包括将任意数量的种子值映射到其内部状态固定数量的机制。它允许您直接设置状态(使用624个值),并为方便起见,提供了从单个32位值到完整的624元素状态的内置映射。


如果您想使用任意数量的输入种子值,则需要实现从任意大小序列到624元素状态的映射。
请记住,映射应该被设计成产生mersenne twister算法的“好”状态。这个算法是基于移位寄存器的,可能会受到产生相对可预测输出的内部状态的影响。单值的内置映射旨在最小化此问题,您实现的任何内容也应如此。
实现这样的映射的最好方法,而不是自己进行数学分析,是简单地使用标准的mersenne twister预热算法。根据this answer上的评论,C++11的std::seed_seq被指定为执行这样的预热。Boost包括boost::seed_seq,它可能做同样的事情。

更新:不需要使用任意数量的值来计算624个值的序列,您可以直接使用624个随机值。如果这些值是无偏的,并且均匀分布在32位值范围内,则不需要进行任何预热(除非您极其不幸)。

boost <random>库可以直接支持此功能:

boost::random_device rd;
boost::mt19937 eng(rd);

请注意,C++11的<random>库不支持使用此种方式进行种子设置。

4
据称自Boost 1.47版本起,Boost库已经拥有了一个名为seed_seq的对象。你可以在这里查看相关资料:seed_seq - T.C.

3

为此,存在一个种子序列生成器辅助程序:

类seed_seq存储一系列32位字来为伪随机数生成器设置种子。这些字将被组合以填充生成器的整个状态。http://www.boost.org/doc/libs/1_57_0/doc/html/boost/random/seed_seq.html

#include <boost/random.hpp>
#include <boost/random/seed_seq.hpp>

int main()
{
    boost::random::seed_seq ss({1ul, 2ul, 3ul, 4ul});
    boost::mt19937 gen2(ss);
}

您还可以将现有范围的一对迭代器传递给它。 在Coliru上实时查看

2
标准的Mersenne Twister类型定义如下:
typedef mersenne_twister_engine mt19937;
第一个模板类型称为UintType,用作种子方法的参数:
void seed(UIntType value);
因此,您可以使用预定义的boost::mt19937_64来获得64位种子。如果您想更多地自定义它,还可以创建自己的mersenne_twister_engine。

7
如果您想要更加自定义,您也可以创建自己的mersenne_twister_engine,但请注意不要这样做。 - T.C.

0

这不是很美观,但应该可以实现:

std::vector<unsigned int> seedv(1000);
seedv[0] = 1;
seedv[1] = 2;
seedv[2] = 3;
seedv[3] = 4;
// etc.
boost::mt19937 gen2(seedv.begin(), seedv.end());

也就是说,即使大部分种子都是0,仍然传入1000个种子。然而,正如@bames53在评论中提到的那样,这不是一个好主意。虽然合法且可以编译,但并不是一个好的种子。

顺便说一下,数组方法应该可以工作(至少应该可以编译):

unsigned long seedv[4];
seedv[0]=1;
seedv[1]=2;
seedv[2]=3;
seedv[3]=4;
boost::mt19937 gen2(seedv, seedv + 4);

这是使用指针作为迭代器的示例。4通常不是迭代器,但seedv + 4是(它是seedv结束后紧接着的元素的地址)。

2
这种方式初始化内部状态并不是一个好主意。https://dev59.com/I2Up5IYBdhLWcg3wHUvi - bames53
谢谢。我已经更新了答案。有时候我回答的是被问到的问题,有时候我回答的是应该被问到的问题。在这种情况下,其他人回答了应该被问到的问题。 - Max Lybbert

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