背景:我在代码中使用rand()
,std::rand()
,std::random_shuffle()
和其他函数进行科学计算。为了能够重现我的结果,我总是明确指定随机种子,并通过srand()
进行设置。直到最近,当我发现libxml2还会在第一次使用时懒散地调用srand()
- 这是在我早期的srand()
调用之后。
我填写了bug report to libxml2 about its srand()
call,但我得到了答案:
首先初始化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);
可能唯一干净的解决方案是根本不使用它,只使用我自己的随机生成器(可能通过C++11 <random>
)。但这不是真正的问题。 问题是,谁应该致电srand()
,如果每个人都这样做,rand()
如何有用?
答案 0 :(得分:33)
请改用新的<random>
标头。它允许多个引擎实例,使用不同的算法,更重要的是,为您提供独立的种子。
[编辑]
回答&#34;有用的&#34;部分,rand
生成随机数字。这有什么好处。如果您需要细粒度控制(包括可重复性),您不仅应该拥有已知种子,还应该使用已知算法。 srand
充其量只会为您提供固定的种子,因此无论如何都不是完整的解决方案。
答案 1 :(得分:26)
嗯,显而易见的是其他人已经说过几次,使用新的C ++ 11生成器。不过,我会以不同的理由重述它
你使用输出进行科学计算,而rand
通常会实现一个相当差的生成器(同时,许多主流实现使用MT19937除了糟糕的状态恢复之外不是这样的不好,但你无法保证特定的算法,至少有一个主流编译器仍然使用非常差的LCG。)
不要用糟糕的发电机进行科学计算。如果你在你的手机上拍摄一些愚蠢的游戏小鸟,如果你的随机数字中有超平面这样的东西并不重要,那么科学模拟的重要时间就很重要了。不要使用坏的发电机。唐&#39;吨
重要提示:std::random_shuffle
(包含两个参数的版本)实际上可能会调用rand
,如果您正在使用该版本,这是一个需要注意的陷阱,即使您使用了<random>
中的新C ++ 11生成器。
关于实际问题,调用srand
两次(甚至更频繁)是没有问题的。原则上,您可以根据需要随时调用它,它所做的只是更改种子,以及随后的伪随机序列。我想知道为什么一个XML库根本不想调用它,但是他们在他们的响应中是正确的,他们这样做并不是非法的。但这也无关紧要
确保唯一重要的是要么你不关心任何特定的伪随机序列(也就是说,任何序列都会这样做,你&#39;对再现一个确切的序列不感兴趣),或你是最后一个呼叫srand
的人,它将覆盖任何先前的呼叫。
那就是说,实现你自己的发电机具有良好的统计特性和足够长的时间段,在3-5行代码中也不是那么难,只需要小心。除了速度之外,主要优点是您可以精确控制您的状态以及修改状态
您不太可能需要比2 128 更长的时段,因为实际消耗这么多数字的时间绝对是禁止的。在2 128 期间,每个周期消耗一个一个的3GHz计算机将持续10 21 年,因此不太多平均寿命的人类问题。即使假设你运行模拟的超级计算机的速度要快一万亿倍,你的盛大孩子也不会活着看到这个时期的结束。
就像2 19937 这样的时期而言,这是现有的&#34;最先进的技术。发电机交付真的很荒谬,如果你问我,那就试图在错误的一端改进发电机(确保它们在统计上更加坚固并且从最坏的情况中迅速恢复时,它会更好 - 案件状态等)。但当然,意见可能会有所不同。
This site列出了几个带有实现的快速生成器。他们的xorshift发生器结合了一个加法或乘法步骤和一个小的(从2到64个机器字)滞后,这导致快速和高质量的发生器(还有一个测试套件,以及网站& #39;作者也写了几篇关于这个主题的论文。我自己修改其中一个(2字128位版本移植到64位,相应地修改了三元组)。
答案 2 :(得分:8)
这个问题正在C ++ 11的随机数生成中解决,即你可以创建一个类的实例:
std::default_random_engine e1
允许您完全控制从对象e1
生成的随机数(与libxml中使用的任何内容相对)。一般的经验法则是使用新构造,因为您可以独立生成随机数。
为了解决您的顾虑 - 我还认为在像libxml这样的库中调用srand()
是不好的做法。但是,更多的是srand()
和rand()
并不是设计用于您尝试使用它们的上下文 - 当您需要一些随机数时它们就足够了,就像libxml那样。但是,当您需要可重复性并确保自己独立于其他人时,新的<random>
标题是您的最佳选择。所以,总而言之,我认为这不是图书馆方面的好习惯,但很难责怪他们这样做。此外,我无法想象他们会改变这种情况,因为其他十亿个软件可能依赖它。
答案 3 :(得分:6)
这里真正的答案是,如果您想确保您的随机数序列不被其他人的代码更改,您需要一个对您的工作而言属于私密的随机数字上下文。请注意,调用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);
}
并且只需拨打myrand
而不是std::rand
(并且应该可以使用随机生成器参数的std::random_shuffle
)