【问题标题】:Distinct rand() sequences yielding the same results in an expression不同的 rand() 序列在表达式中产生相同的结果
【发布时间】:2010-04-04 17:32:05
【问题描述】:

好吧,这真的很奇怪。

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

【问题讨论】:

  • 检查是否真的是 rand() 问题: log
  • 在每种情况下打印您正在播种 srand() 的值。
  • 那是 Neil:进程 1 是 X,进程 2 是 X+1,进程 3 是 X+2 等等。
  • 您的新公式(从 EDIT1 开始)看起来是正确的。您能否更改循环内的输出语句,同时将rank 的值和用于计算waitSecsrand() 的值提供给我们?

标签: c++ random


【解决方案1】:

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

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

【讨论】:

  • 我从该站点使用更大范围和更好的方法进行了测试,结果相同(请参阅问题中的编辑)。
【解决方案2】:

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

您使用的rand() 可能是linear congruential generator。如果您点击后一个链接,您会发现更多关于它是如何工作的信息,以及为什么低位比高位“随机性更小”的解释。

【讨论】:

    【解决方案3】:

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

    哎哟。

    抱歉浪费了大家的时间。

    【讨论】:

    • 如果您将 rand 与多线程应用程序一起使用,您可能还想查看线程安全版本 rand_r,它应该是 POSIX 指定的可重入,其中 rand 不是必需的(但可能取决于实施)。
    • 我认为您可以“接受”您自己的答案作为正确答案。这会将它冒泡到顶部,并防止任何阅读此线程的人跟随红鲱鱼。
    • 2天不让我接受答案,一定是新功能。另外,我实际上使用的是 Qt 的 qrand(),它声称是 rand() 的线程安全版本。
    【解决方案4】:

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

    编辑:例如,低质量可能意味着即使在[0,100]^2 中生成二维点(x,y) 也会导致点在正方形中分布不均匀。你可能认为它不应该表现得那么糟糕,但你会惊讶于普通 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();
    

    编辑3:
    (来源:dilbert.com

    【讨论】:

    • 您还可以看到,前三个数字的 %9 似乎相等:只是 rand() 质量太低,以至于您的序列高度相关。
    • 我不需要高质量的随机数,当然rand() 的表现会比这更好。我可能会尝试其他方法,但我仍然想知道是什么导致了这种现象。
    • 也许吧,但低质量的随机数根本不是随机的 :)。请注意,高质量的随机数生成器也可能非常快。
    猜你喜欢
    • 1970-01-01
    • 2016-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多