好的,这真的很奇怪。
我有一个MPI程序,每个进程必须生成固定范围内的随机数(范围从文件中读取)。会发生的是,即使我使用不同的值为每个进程设定种子,并且rand()
生成的数字在每个进程中都不同,生成随机数的表达式仍会在它们之间产生相同的序列。
以下是所有相关代码:
// 'rank' will be unique for each process
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
// seed the RNG with a different value for each process
srand(time(NULL) + rank);
// print some random numbers to see if we get a unique sequence in each process
// 'log' is a uniquely named file, each process has its own
log << rand() << " " << rand() << " " << rand() << std::endl;
// do boring deterministic stuff
while (true)
{
// waitTimeMin and waitTimeMax are integers, Max is always greater than Min
waitSecs = waitTimeMin + rand() % (waitTimeMax - waitTimeMin);
log << "waiting " << waitSecs << " seconds" << std::endl;
sleep(waitSecs);
// do more boring deterministic stuff
}
这是每个过程的输出,其中3个过程生成[1,9]范围内的数字。
流程1:
15190 28284 3149
waiting 6 seconds
waiting 8 seconds
waiting 9 seconds
waiting 4 seconds
流程2:
286 6264 3153
waiting 6 seconds
waiting 8 seconds
waiting 9 seconds
waiting 4 seconds
流程3:
18151 17013 3156
waiting 6 seconds
waiting 8 seconds
waiting 9 seconds
waiting 4 seconds
因此,虽然rand()
明确生成不同的数字,但计算waitSecs
的表达式仍会在所有进程上评估相同的序列。甚至更奇怪的是:如果我再次使用相同的参数运行程序,只会改变前3个随机数,其余的“随机”序列在每次运行中都会完全相同!改变数字的范围显然会产生与此不同的结果,但是相同的参数总是在执行之间的进程和之间产生相同的顺序:除了前3个数字。
这到底是怎么回事?
编辑:所以只是为了看它是简单的随机生成和/或低范围,我用这一行替换了随机生成:
waitSecs = waitTimeMin + (int)((double)rand() / ((double)RAND_MAX + 1) * (waitTimeMax - waitTimeMin));
并开始生成[1,99]范围内的数字。结果如下:
流程1:
7833 3798 10977
waiting 1 seconds
waiting 20 seconds
waiting 58 seconds
waiting 35 seconds
waiting 82 seconds
waiting 18 seconds
流程2:
25697 14547 10980
waiting 1 seconds
waiting 20 seconds
waiting 58 seconds
waiting 35 seconds
waiting 82 seconds
waiting 18 seconds
流程3:
10794 25295 10984
waiting 1 seconds
waiting 20 seconds
waiting 58 seconds
waiting 35 seconds
waiting 82 seconds
waiting 18 seconds
同样的事情。这仍然只是rand()
真的很糟糕吗?
EDIT2:生成从1到10000的数字时也是如此。
答案 0 :(得分:4)
在您的代码中,如果生成的随机数(除数除以8),则只使用3个低位。您的实验表明,每次生成的数字序列的最低3位序列是相同的。这是完全可能的。事实上,这是一个众所周知的问题,通常用于实现rand()
的简单伪随机数生成器。
如果您想使用rand()
(而不是更复杂的自定义生成器),最好使用高阶位,而不是低阶位。即不要使用%
运算符来缩小rand()
的范围。看看这里有一个更好的方法:http://c-faq.com/lib/randrange.html
答案 1 :(得分:1)
尝试使用tr1中的随机数生成器,如std::tr1::mt19937
。 rand()
函数通常使用低质量随机数生成器实现。
编辑:低质量可能意味着,即使在(x,y)
中生成2D点[0,100]^2
,也会导致在广场中均匀分散的点。你可能认为它不应该表现得那么糟糕,但你会惊讶于rand()
实际上表现得多么糟糕。(在大多数语言中这都是可悲的)。
EDIT2:方法range*(rand()/RAND_MAX)
不是一个好方法。它存在双精度问题,甚至不会产生结果。
请尝试以下操作,看看您的程序是否仍能为您提供令您惊讶的结果:
std::tr1::mt19937 engine(thread_seed);
std::tr1::uniform_int<> unigen(waitTimeMin, waitTimeMax);
std::tr1::variate_generator<std::tr1::mt19937,
std::tr1::uniform_int<> >gen(engine, unigen);
waitSec = gen();
答案 2 :(得分:1)
计算(rand() % n)
通常是一个坏主意 - 你得到的结果不是随机的。相反,如果RAND_MAX
是rand()
的输出范围,请尝试将rand()
除以(RAND_MAX/(waitTimeMax - waitTimeMin))
。
您使用的rand()
可能是linear congruential generator。如果您按照后一个链接,您将找到有关其工作原理的更多信息,并解释为什么较低位比较高位更“随机”。
答案 3 :(得分:1)
srand()
修复了问题。所以是的,这里的教训是srand()
和rand()
每个线程工作,而不是每个进程。我还需要在我的问题中开始发布有关我的计划的更多信息。
哎哟。
抱歉浪费每个人的时间。