【问题标题】:C++11 cross compiler/standard library random distribution reproducibilityC++11 交叉编译器/标准库随机分布再现性
【发布时间】:2014-12-19 17:50:06
【问题描述】:

虽然随机引擎需要在每个编译器上提供相同的数字序列。至少一些随机分布不是,只要求它们满足统计和概率阈值。举个例子:

#include <random>
#include <iostream>

int main() {
  std::mt19937 foo;
  std::uniform_int_distribution<int> bar(0, 1000);

  for (int i=0; i<99; ++i) {
    bar(foo);
  }

  std::cout << bar(foo) << std::endl;

  return 0;
}

当针对(我的版本)libstdc++ 编译时将打印 808,当针对 libc++ 编译时将打印 89。

无论为您提供何种合规环境,哪些标准提供的分发功能(如果有)可以保证产生一致的结果?

【问题讨论】:

  • 89 比 808 随机得多,相信我。 ;-)
  • 当然不像 4 那样随机。 xkcd.com/221
  • +1 用于优雅地处理我的笑话。 :-)

标签: c++ c++11 random language-lawyer


【解决方案1】:

不幸的是,从 N3936(C++14 最终草案)开始,没有任何标准提供随机分布有这样的要求。很容易看出原因。有许多编写分布函数的有效方法。有些比其他更好。甚至像正态分布这样基本的算法也在变得越来越好,并且是积极研究的主题。强制使用单个算法会不必要地阻碍未来算法的实施。

幸运的是,您可以自己编写。各种分发类的标头规范位于 §26.5.8 下。但是没有理由你必须遵循这种结构。

(请注意,我没有彻底测试过这段代码,并且某些引擎或溢出可能会出现不良行为,尽管我已经尽力避免后者,这更多是作为说明性示例而不是令人敬畏的均匀分布的规范来源。话虽如此,如果您发现它有任何问题,请在 cmets 中告诉我,我很乐意纠正它。)

#include <random>
#include <tuple>
#include <iostream>

template<class IntType = int>
class my_uniform_int_distribution {
public:
  // types
  typedef IntType result_type;
  typedef std::pair<int, int> param_type;

  // constructors and reset functions
  explicit my_uniform_int_distribution(IntType a = 0, IntType b = std::numeric_limits<IntType>::max());
  explicit my_uniform_int_distribution(const param_type& parm);
  void reset();

  // generating functions
  template<class URNG>
    result_type operator()(URNG& g);
  template<class URNG>
    result_type operator()(URNG& g, const param_type& parm);

  // property functions
  result_type a() const;
  result_type b() const;
  param_type param() const;
  void param(const param_type& parm);
  result_type min() const;
  result_type max() const;

private:
  typedef typename std::make_unsigned<IntType>::type diff_type;

  IntType lower;
  IntType upper;
};

template<class IntType>
my_uniform_int_distribution<IntType>::my_uniform_int_distribution(IntType a, IntType b) {
  param({a, b});
}

template<class IntType>
my_uniform_int_distribution<IntType>::my_uniform_int_distribution(const param_type& parm) {
  param(parm);
}

template<class IntType>
void my_uniform_int_distribution<IntType>::reset() {}

template<class IntType>
template<class URNG>
auto my_uniform_int_distribution<IntType>::operator()(URNG& g) -> result_type {
  return operator()(g, param());
}

template<class IntType>
template<class URNG>
auto my_uniform_int_distribution<IntType>::operator()(URNG& g, const param_type& parm) -> result_type {
  diff_type diff = (diff_type)parm.second - (diff_type)parm.first + 1;
  if (diff == 0) // If the +1 overflows we are using the full range, just return g()
    return g();

  diff_type badDistLimit = std::numeric_limits<diff_type>::max() / diff;
  do {
    diff_type generatedRand = g();

    if (generatedRand / diff < badDistLimit)
      return (IntType)((generatedRand % diff) + (diff_type)parm.first);
  } while (true);
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::a() const -> result_type {
  return lower;
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::b() const -> result_type {
  return upper;
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::param() const -> param_type {
  return {lower, upper};
}

template<class IntType>
void my_uniform_int_distribution<IntType>::param(const param_type& parm) {
  std::tie(lower, upper) = parm;
  if (upper < lower)
    throw std::exception();
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::min() const -> result_type {
  return lower;
}

template<class IntType>
auto my_uniform_int_distribution<IntType>::max() const -> result_type {
  return upper;
}

int main() {
  std::mt19937 foo;
  my_uniform_int_distribution<int> bar(0,1000);

  for (int i=0; i<99; ++i) {
    bar(foo);
  }

  std::cout << bar(foo) << std::endl;

  return 0;
}

这段代码在我测试过的所有平台上都打印出 490。

【讨论】:

  • N3797 不是 C++14 的最终草案。 N3936 是。
  • @T.C.:从技术上讲,N4141 (open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4139.html)。不同之处只是少数编辑修复。这些都不是公开的。 :-(
  • 这段代码非常接近正确,但它假定URNG::max() == std::numeric_limits&lt;diff_type&gt;::max()URNG::min() == 0。一些 PRNG 引擎的 min() 为 1。
猜你喜欢
  • 2017-03-09
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多