不同的rand()序列在表达式中产生相同的结果

时间:2010-04-04 17:32:06

标签: c++ random

好的,这真的很奇怪。

我有一个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的数字时也是如此。

4 个答案:

答案 0 :(得分:4)

在您的代码中,如果生成的随机数(除数除以8),则只使用3个低位。您的实验表明,每次生成的数字序列的最低3位序列是相同的。这是完全可能的。事实上,这是一个众所周知的问题,通常用于实现rand()的简单伪随机数生成器。

如果您想使用rand()(而不是更复杂的自定义生成器),最好使用高阶位,而不是低阶位。即不要使用%运算符来缩小rand()的范围。看看这里有一个更好的方法:http://c-faq.com/lib/randrange.html

答案 1 :(得分:1)

尝试使用tr1中的随机数生成器,如std::tr1::mt19937rand()函数通常使用低质量随机数生成器实现。

编辑:低质量可能意味着,即使在(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();

EDIT3: http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/000000/00000/2000/300/2318/2318.strip.gif

答案 2 :(得分:1)

计算(rand() % n)通常是一个坏主意 - 你得到的结果不是随机的。相反,如果RAND_MAXrand()的输出范围,请尝试将rand()除以(RAND_MAX/(waitTimeMax - waitTimeMin))

您使用的rand()可能是linear congruential generator。如果您按照后一个链接,您将找到有关其工作原理的更多信息,并解释为什么较低位比较高位更“随机”。

答案 3 :(得分:1)

好吧,显然我很迟钝。在初始化RNG之后,我生成了一个新线程并在那里生成了随机数,没有初始化。在新线程中调用srand()修复了问题。所以是的,这里的教训是srand()rand()每个线程工作,而不是每个进程。我还需要在我的问题中开始发布有关我的计划的更多信息。

哎哟。

抱歉浪费每个人的时间。