Alb*_*ert 46 c c++ random srand
背景:我使用rand(),std::rand(),std::random_shuffle()和我的科学计算代码等功能.为了能够重现我的结果,我总是明确指定随机种子,并通过它设置srand().直到最近,当我发现libxml2也会srand()在第一次使用时懒得打电话- 这是在我早期的srand()电话之后.
我填写了一个关于它的srand()调用的错误报告到libxml2,但我得到了答案:
首先初始化libxml2.这是一个完全合法的电话会议.您不应该期望没有其他人调用
srand(),并且手册页无处声明srand()应该避免使用多个时间
这实际上是我现在的问题.如果一般政策是每个lib可以/应该/将要调用srand(),并且我可以/也可以在这里和那里调用它,我真的不知道它是如何有用的.或者如何rand()有用呢?
这就是为什么我认为,一般(不成文)策略是没有lib应该调用srand(),应用程序应该在开始时只调用它一次.(不考虑多线程.我想在这种情况下,你无论如何应该使用不同的东西.)
我也尝试研究其他库实际调用的一些srand(),但我没有发现任何.有吗?
我目前的解决方法是这个丑陋的代码:
{
// On the first call to xmlDictCreate,
// libxml2 will initialize some internal randomize system,
// which calls srand(time(NULL)).
// So, do that first call here now, so that we can use our
// own random seed.
xmlDictPtr p = xmlDictCreate();
xmlDictFree(p);
}
srand(my_own_seed);
Run Code Online (Sandbox Code Playgroud)
可能唯一干净的解决方案是根本不使用它,只使用我自己的随机生成器(可能通过C++ 11<random>).但这不是真正的问题.问题是,谁应该打电话srand(),如果每个人都这样做,那么它rand()有用吗?
MSa*_*ers 33
请改用新<random>标头.它允许多个引擎实例,使用不同的算法,更重要的是,为您提供独立的种子.
[edit]要回答"有用"部分,请rand 生成随机数.这就是它的好处.如果您需要细粒度控制(包括可重复性),则不仅应该具有已知种子,还应该使用已知算法.srand最好给你一个固定的种子,所以这不是一个完整的解决方案.
Dam*_*mon 26
好吧,显而易见的是其他人已经说过几次,使用新的C++ 11生成器.不过,我正在以不同的理由重申它.
你使用输出进行科学计算,并且rand通常实现一个相当差的生成器(同时,许多主流实现使用MT19937除了糟糕的状态恢复之外没有那么糟糕,但是你不能保证特定的算法,并且至少有一个主流编译器仍然使用非常差的LCG).
不要用劣质发电机进行科学计算.如果你在你的手机上做一些愚蠢的游戏拍摄小鸟,你的随机数字中是否有像超平面这样的东西并不重要,但这对科学模拟来说很重要.不要使用坏的发电机.别.
重要提示:( std::random_shuffle具有两个参数的版本)可能实际调用rand,即使您使用的是新的C++ 11生成器,如果您正在使用该错误,也需要注意这个陷阱<random>.
关于实际问题,调用srand两次(甚至更频繁)是没有问题的.原则上,您可以根据需要随时调用它,它所做的只是更改种子,以及随后的伪随机序列.我想知道为什么XML库根本不想调用它,但是他们的响应是正确的,他们这样做并不是非法的.但这也没关系.
要确保唯一重要的是要么你不关心得到任何特定的伪随机序列(也就是说,任何序列都会这样做,你对再现一个确切的序列不感兴趣),或者你是最后一个srand,它将覆盖任何先前的调用.
也就是说,实现自己的发电机具有良好的统计特性和足够长的时间段,在3-5行代码中也不是那么难,只需要小心.除了速度之外,主要优势在于您可以精确控制您的状态以及修改状态.由于实际消耗这么多数字的绝对禁止时间
,你不太可能需要超过2 128的时段.每个周期消耗一个数字的3GHz计算机将在2 128个周期内运行10 21年,因此对于具有平均寿命的人来说没有太大问题.即使假设您运行模拟的超级计算机的速度要快一万亿倍,您的盛大孩子也不会活着看到这个时期的结束.
就目前的"最先进"发电机所提供的2 19937这样的时期来说真的很荒谬,如果你问我那就试图改善发电机的错误结束(确保它们在统计上更加稳固并且它们能够迅速恢复从最坏情况的状态等).但当然,意见可能会有所不同.
该站点列出了几个带有实现的快速生成器.他们是xorshift发生器结合加法或乘法步骤和一个小的(从2到64个机器字)滞后,这导致快速和高质量的生成器(也有一个测试套件,该网站的作者写了几个关于这个问题的论文也是如此).我正在使用其中一个的修改(2字128位版本移植到64位,相应地修改了三元组).
小智 8
这个问题正在C++ 11的随机数生成中解决,即你可以创建一个类的实例:
std::default_random_engine e1
Run Code Online (Sandbox Code Playgroud)
它允许您完全控制从对象生成的随机数e1(而不是在libxml中使用的任何内容).一般的经验法则是使用新构造,因为您可以独立生成随机数.
为了解决您的顾虑 - 我也认为调用srand()像libxml这样的库是不好的做法.但是,它更多的是srand(),rand()并不是设计用于你试图使用它们的上下文 - 当你只需要一些随机数时它们就足够了,就像libxml那样.但是,当您需要可重复性并确保您独立于其他人时,新<random>标题是您的最佳选择.所以,总而言之,我认为这不是图书馆方面的好习惯,但很难责怪他们这样做.此外,我无法想象他们会改变这一点,因为其他十亿个软件可能依赖于它.
这里真正的答案是,如果你想确定你的随机数序列没有被其他人的代码改变,你需要一个随机数字上下文,这对你的工作是私有的.请注意,呼叫srand只是其中的一小部分.例如,如果在其他调用的库中调用某个函数rand,它也会破坏您的随机数序列.
换句话说,如果您希望基于随机数生成从代码中获得可预测的行为,则需要将其与使用随机数的任何其他代码完全分开.
其他人建议使用C++ 11随机数生成,这是一种解决方案.
在Linux和其他兼容的库上,您也可以使用rand_r,它将指针指向unsigned int用于该序列的种子.因此,如果您初始化该seed变量,然后将其用于所有调用rand_r,它将为您的代码生成唯一的序列.这当然仍然是同一个老rand发电机,只是一个单独的种子.我的意思是这个主要原因是你可以很容易地做到这样的事情:
int myrand()
{
static unsigned int myseed = ... some initialization of your choice ...;
return rand_r(&myseed);
}
Run Code Online (Sandbox Code Playgroud)
并简单地调用myrand而不是std::rand(并且应该可以std::random_shuffle使用随机生成器参数)