C ++ 11交叉编译器/标准库随机分布的重现性 [英] C++11 cross compiler/standard library random distribution reproducibility

查看:96
本文介绍了C ++ 11交叉编译器/标准库随机分布的重现性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  #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 ++和89



无论您给予何种合规环境,哪些标准提供的分布函数(如果有)都能保证产生一致的结果?

$不幸的是,从N3936(C ++ 14最终草案),没有标准提供的随机分布有这样的要求。很容易看到为什么。有许多有效的写分布函数的方法。一些比别人更好。并且算法甚至一些基本的正常分布越来越好,是主动研究的主题。强制使用单一的会不必要地阻碍未来算法的实施。



幸运的是,你可以自己写。各种分发类的头的规范位于§26.5.8下。但是没有理由为什么你的需要遵循这个结构必然。



(请注意,我没有彻底测试这个代码,并且某些引擎可能有不良行为,或者与溢出,虽然我已经采取了一些痛苦,以避免后者,这只是一个说明性的例子,而不是一个典型的真棒均匀分布的来源。据说,如果你发现任何错误,让我知道在评论,我很乐意纠正它。)

  #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;

//构造函数和重置函数
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();

//生成函数
template< class URNG>
result_type operator()(URNG& g);
template< class URNG>
result_type operator()(URNG& g,const param_type& parm);

//属性函数
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)//如果+1溢出,我们使用全范围,只是返回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。 >

While the random engines are required to give the same sequence of numbers on each compiler. At least some of the random distributions are not, only requiring that they meet statistical and probabilistic thresholds. As an example:

#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;
}

Which will print 808 when compiled against (my version of) libstdc++ and 89 when compiled against libc++.

Which of the standard provided distribution functions, if any, are guaranteed to produce consistent results no matter what compliant environment you are given?

解决方案

Unfortunately, as of N3936 (C++14 final draft), no standard provided random distribution has a requirement like this. And it's easy to see why. There are many valid ways of writing a distribution function. Some better than others. And algorithms for even something as basic as the normal distribution are getting better and are the subject of active research. Mandating use of a single one would unnecessarily roadblock the implementation of future algorithms.

Fortunately, you can write your own. The specification for the headers of the various distribution classes are located under §26.5.8. But there is no reason why yours needs to follow this structure necessarily.

(Please note, I have not tested this code thoroughly, and there might be bad behavior with certain engines, or with overflow, though I have taken some pains to avoid the latter, this is intended more as an illustrative example than a canonical source of awesome uniform distribution. That being said, if you find anything wrong with it, let me know in the comments and I'll be happy to correct it.)

#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;
}

This code prints out 490 on all platforms I've tested.

这篇关于C ++ 11交叉编译器/标准库随机分布的重现性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆