C ++ 11与不同类型的随机分布共享相同的功能

时间:2018-06-01 00:58:40

标签: c++ c++11

我有两个功能。这两个函数的唯一区别是它们使用不同的随机数生成器。为方便起见,我写了一些可编译的代码,如下所示。这两个功能是

  

fillRandomVecInt

  

fillRandomVecNorm

我们如何将这两个功能合二为一?我可以使用指向基类的指针并动态连接rnormrunifInt吗?或使用模板来解决此问题?非常感谢你的帮助。

#include <iostream>
#include<random>
using namespace std;

class RandomVec{
private:
    unsigned seed = 0;
    std::default_random_engine generator{seed};
    std::normal_distribution<> rnorm{0,1};
    std::uniform_int_distribution<> runifInt{0,1};
public:

    void fillRandomVecInt(vector<int> & v);
    void fillRandomVecNorm(vector<double> & v);
};

void RandomVec::fillRandomVecNorm(vector<double> &v) {
    for(auto i=v.begin();i!=v.end();++i){
        *i=rnorm(generator);
    }
}

void RandomVec::fillRandomVecInt(vector<int> &v) {
    for(auto i=v.begin();i!=v.end();++i){
        *i=runifInt(generator);
    }
}


int main() {
    RandomVec rv;
    vector<double> x=vector<double>(10);
    rv.fillRandomVecNorm(x);
    vector<int> y = vector<int>(10);
    rv.fillRandomVecInt(y);
    return 0;
}

4 个答案:

答案 0 :(得分:3)

我建议使用模板专业化。

class RandomVec {
private:
  unsigned seed = 0;
  std::default_random_engine generator{seed};
  std::normal_distribution<> rnorm{0, 1};
  std::uniform_int_distribution<> runifInt{0, 1};

  template<typename T>
  T generateRandom(void);

public:
  template<typename T>
  void fillRandomVec(vector<T> &v);
};

template<>
int RandomVec::generateRandom(void) {
  return runifInt(generator);
}

template<>
double RandomVec::generateRandom(void) {
  return rnorm(generator);
}

template<typename T>
void RandomVec::fillRandomVec(vector<T> &v) {
  for (auto i = v.begin(); i != v.end(); ++i) {
    *i = generateRandom<T>();
  }
}

答案 1 :(得分:1)

也许我误解了你的要求,但你不能只使用函数重载吗?

void fillRandomVec(vector<int>& v) {
    //code...
}

void fillRandomVec(vector<double>& v {
    //code...
}

或者...

//this is a function I have used that I copied in
template<class T>
bool vectIsInt(std::vector<T>& v)
{
    for (int i = 0; i < v.size(); i++)
    {
        if (v[i] != floor(v[i]) return false;
    }
    return true;
}

template<class U>
void fillRandomVec(std::vector<U>& v)
{
    if (vectIsInt(v))
    {
        //code if int vect
    }
    else
    {
        //code if double vect...
    }
}

答案 2 :(得分:0)

#include<time.h>
#include<algorithm>
#include<functional>
#include <iostream>
#include<random>
using namespace std;

namespace stackoverflow
{
    //just for this topic

    template<typename _FwdIt,typename _RngFn>
    void random_fill(_FwdIt first, _FwdIt last, _RngFn&& fn)
    {   //random fill the range [first,last) by means of fn
        _DEBUG_RANGE(first, last);
        generate(first, last, fn);
    }
}

//random function-fn is provided by yourself, for example

int main() {
    using namespace stackoverflow;

    static default_random_engine e(time(0));
    std::normal_distribution<> rnorm{ 0,1 };
    std::uniform_int_distribution<> runifInt{ 0,1 };
    std::uniform_int_distribution<> runifInt_1{ 2,10 };
    std::uniform_real_distribution<> runifDouble{ 0,1 };

    vector<int> x(10);
    //type int can be random fill throw normal or uniform distribution or other, and 
    //some distribution parameter can change
    random_fill(x.begin(), x.end(), bind(ref(runifInt),ref(e)));
    random_fill(x.begin(), x.end(), bind(ref(runifInt_1), ref(e)));
    random_fill(x.begin(), x.end(), bind(ref(rnorm), ref(e)));

    vector<double> y(10);
    //type double can be random fill throw normal or uniform distribution or other, and
    //some distribution parameter can change
    random_fill(y.begin(), y.end(), bind(ref(rnorm),ref(e)));
    random_fill(y.begin(), y.end(), bind(ref(runifDouble), ref(e)));

    return 0;
}

我认为STL算法是最有帮助的,所以我设计了这些变异STL。   对于main函数中定义的对象,您可以根据需要将它们移动到stackoverflow命名空间中,但不要将它们全局化。

答案 3 :(得分:0)

您可以模拟基类,该基类的特化根据其typename参数提供不同的分发对象。

然后,对于您想要支持的每种类型,只需要少量专门使用相应分发对象的类模板的代码。

这种方法的一个优点是一个专用基类可以支持多个typename T。例如,在下面的示例中,支持所有整数(不仅仅是int)和浮点(不仅仅是double)类型。

请参阅以下代码:

#include <iostream>
#include<random>
using namespace std;

namespace {
// Helper type defined since C++14.
template<bool B, class T = void>
using enable_if_t = typename std::enable_if<B,T>::type;
}

// Base class to be specialized with different distribution object.
template<typename T, typename Enable = void>
class RandomVecBase;

// Supports integers with uniform_int_distribution
template<typename T>
class RandomVecBase<T, enable_if_t<std::is_integral<T>::value> > {
 public:
  RandomVecBase() : distrib_{0, 1} {}
 protected:
  uniform_int_distribution<> distrib_;
};

// Supports floating-point with normal_distribution.
template<typename T>
class RandomVecBase<T, enable_if_t<std::is_floating_point<T>::value> > {
 public:
  RandomVecBase() : distrib_{0, 1} {}
 protected:
  normal_distribution<> distrib_;
};

// Actual class, code in fillRandomVec() only needs to be written once
template<typename T>
class RandomVec : public RandomVecBase<T> {
 private:
  unsigned seed = 0;
  default_random_engine generator{seed};

 public:
  void fillRandomVec(vector<T>& v) {
    for (auto& i : v) {
      i = this->distrib_(generator);
    }
  }
};


int main() {
  RandomVec<double> rv_double;
  vector<double> x = vector<double>(10);
  rv_double.fillRandomVec(x);

  RandomVec<int> rv_int;
  vector<int> y = vector<int>(10);
  rv_int.fillRandomVec(y);
  return 0;
}