<random> 不同计算机之间的C++行为不一致

Inconsistent C++ <random> behavior between different computers

本文关键字:C++ 不一致 之间 lt random gt 计算机      更新时间:2023-10-16

我已经实现了一个c++解决方案,用于测试针对一系列随机事件的各种策略。我正在聚合在几台计算机上运行的多线程模拟的结果。

单个模拟产生一个整数结果,通常需要生成大约100个均匀随机整数,并且在保存聚合数据块(平均值,标准偏差,最小值,最大值)之前重复1,000,000次。尽管在给定的体系结构上,这种块的结果在6位有效数字以内是一致的,但是在两台运行完全相同程序的计算机之间,差异要大几个数量级。

到目前为止,我在两台带有英特尔处理器的个人Windows笔记本电脑和一个AWS c3.8xlarge Windows Server实例上运行了这个程序(相同的可执行文件)。在每台计算机上,正在进行的模拟很快接近不同的值。平均数之间的相对差异约为10^-3。在一台计算机上,100万个数据块之间的相对均值差很少超过10^-6。

程序使用<random>中的mt19937随机数生成器。我用time(NULL)播种

我想不出这种矛盾的原因。Mersenne Twister被认为是蒙特卡罗模拟的声音发生器,我多次使用它,经常能够分析验证结果。我可以理解由于生成器的缺陷和底层架构而导致的细微差异和一致性的偏离,但以这种数量级,很难理解。

在重构程序并消除不必要的操作后,结果在不同主机之间是一致的。似乎舍入误差在各种看似相似的64位架构之间存在显著差异,并且由于某些设计缺陷,它们的累积导致了我的模拟结果的严重分歧。我要感谢@DanielKO、@TonyD、@amdn和@Yakk提出的宝贵建议。

一个有趣的注意事项:从一开始,c3.8xlarge AWS实例始终提供相同的(正确的)结果。相反,Core 2受到的打击最为严重。

看来你已经能够解决你的问题了。不过,让我指出您的代码中的一两个(可能的)问题。如果没有实际看到任何源代码,很难为您做更多的事情。

  • 不要使用time(NULL)作为种子。它是一个非常低的熵源——这是相当糟糕的。首先,在不同机器上运行的程序的两个实例可能会选择相同的种子,其次,连续两次运行只会有稍微不同的种子,这可能会导致相似或至少相关的随机数。如果您为每个线程创建一个prng(建议这样做!),那么第二点尤其糟糕,因为所有线程可能只是创建相同的数字。至少使用seed_seq,但最好使用真正的随机源(random_device)。
  • 虽然标准保证生成器mt19937将在其32位变体和(明显)更快的64位变体以及不同的硬件和软件版本中提供相同的结果,但发行版确实给您相同的保证。如果你想重现相同的结果,你应该编写自己的发行版(我认为这是标准的一个非常糟糕的设计缺陷,因为编写一个好的发行版是非常重要的……但在你的情况下,这应该是不必要的,只要你只需要随机分布的数字,而不需要重新创建一个特定的数字序列)。

虽然(正如我已经说过的)发行版的差异不太可能导致您的问题,但这是两个符合标准的实现之间唯一的区别。鉴于此,我建议彻底检查您的代码的其余部分,因为<random>库似乎不太可能在这里实际上是错误的。