【问题标题】:std::normal_distribution<double> results in wrong order windows versus linux?std::normal_distribution<double> 导致 windows 与 linux 的顺序错误?
【发布时间】:2015-11-23 17:02:05
【问题描述】:

有人来访问这个问题吗?根据1,实现不需要生成相同的数据。 在实践中,arm、x86、免费和商业编译器之间的 STL 实现有什么不同?

// g++ --std=c++11 -o a minimal.cpp && ./a

#include <iostream>
#include <random>

using namespace std;

int
main()
{
  std::mt19937_64 gen;
  gen.seed(17);

  cout << "\nNormal\n";
  normal_distribution<double> distr1;
  for (int i = 0; i < 2; i++) {
    double delay = distr1(gen);
    printf("  Value = %15.15g\n", delay);
  }
  return(0);
}


/*

Results

(1) gcc-4.8.0 linux 64b version result
  Normal
    Value = 1.03167351251536
    Value = 1.21967569130525
(2) Microsoft Visual Studio Community 2015 Version 14.0.23107.0 D14REL
      or 
    Microsoft Visual Studio Professional 2012 Version 11.0.60610.01 Update 3
  Normal
    Value = 1.21967569130525
    Value = 1.03167351251536  // same values in wrong (different) order
*/

我可以理解在某些特殊硬件平台上对生成器或分发使用不同的算法,但这种差异似乎更像是一个错误。

以下是我用来诊断差异的来源和解决方法的更多代码: - win 和 linux 上的生成器和均匀分布匹配。 - 除成对顺序外,正态分布在数值上匹配

//   g++ --std=c++11 -o a workaround.cpp && ./a


#include <iostream>
#include <random>
#include <stack>


using namespace std;

typedef  std::mt19937_64  RandGenLowType;

// Helper wrapper - it did confirm that the differences
// do NOT come from the generator
class RandGenType : public RandGenLowType {
  public:
    result_type operator()() {
      result_type val = RandGenLowType::operator()();
      printf("    Gen pulled %20llu\n", val);
      return(val);
    }
};


typedef normal_distribution<double> NormalDistrLowType;

// Workaround wrapper to swap the output data stream pairwise
class NormalDistrType : NormalDistrLowType {
  public:
    result_type operator()(RandGenType &pGen) {
      // Keep single flow (used variables, includes) same for all platforms
      if (win64WaStack.empty()) {
        win64WaStack.push(NormalDistrLowType::operator()(pGen));
#ifdef _MSC_VER
        win64WaStack.push(NormalDistrLowType::operator()(pGen));
#endif
      }
      result_type lResult = win64WaStack.top();
      win64WaStack.pop();
      return(lResult);
    }    
  private:
    std::stack<result_type> win64WaStack;
};


int
main()
{
  RandGenType gen;
  gen.seed(17);

  // No platform issue, no workaround used
  cout << "\nUniform\n";
  uniform_real_distribution<double> distr;
  for (int i = 0; i < 4; i++) {
    double delay = distr(gen);
    printf("  Delay = %15.15g\n", delay);
  }

  // Requires the workaround
#ifdef _MSC_VER
  cout << "Workaround code is active, swapping the output stream pairwise\n";
#endif
  cout << "\nNormal\n";
  //normal_distribution<float> distr1;
  NormalDistrType distr1;
  for (int i = 0; i < 10; i++) {
    double delay = distr1(gen);
    printf("  Value = %15.15g\n", delay);
  }
  return(0);
}

【问题讨论】:

    标签: c++ linux random stl


    【解决方案1】:

    几种常见的生成正态分布随机数的方法,例如Box-Muller transformMarsaglia polar method,一次生成两个随机数。使用其中一种方法的分布对象会生成两个随机数,返回其中一个,并保存另一个以供下次调用。

    返回哪个,存储哪个当然完全取决于库作者。看起来 libstdc++ 和 MSVC 使用相同的算法,但碰巧选择不同。

    【讨论】:

    • 只是为了好玩,我在 Mac OS X 上用 libc++ 试用了你的程序。我得到了与 Visual Studio 相同的答案。
    • 好的,谢谢。所以总而言之 - 1)所有 3 个平台都使用相同的算法,2)有问题的是 linux/gcc。
    • @user5278667 我不明白你为什么认为有错误。没有。
    • 该错误可能在规范(标准)中 - 它过于灵活,允许非便携式实现。例如,想象 std::mt19937_64 返回相同种子的平台特定序列。
    猜你喜欢
    • 2015-02-28
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 2020-09-29
    • 1970-01-01
    相关资源
    最近更新 更多