Thursday, December 15, 2016

C++11 : Template Class for Random Generator

"Anyone who attempts to generate random numbers by deterministic means is, 
of course, living in a state of sin." - John Von Neumann

Despite of the fact that the quote above is still highly relevant, we mostly get the job done well enough using those pseudo-random generators for our Monte Carlo stuff. So, this time I wanted to present a wrapper for simulating (sinful) uniform random numbers, which are then mapped to a chosen probability distribution. The main goal was to construct a lightweight design, using C++11 uniform generators, probability distributions as well as some other extremely useful tools (function, lambda) for the task.


TEMPLATE DIET


Instead of implementing any class hierarchies, I tried to keep this design as lightweighted as possible by using template class having only two template data types to be defined by a client : Generator (uniform random generator) and Distribution (probability distribution for transforming uniformly distributed random numbers). In a nutshell, a client may use any random number engine available in C++11, for creating desired amount of uniform random numbers. Moreover, a client may use default probability distribution (Standard Normal) for transforming created uniform random numbers or additionally, use any desired probability distribution available in C++11. Available uniform random engines and probability distributions have been nicely covered in here.


ULTIMATE SIN


It should be a bit easier to sell something, if the final product is nicely presented. Random numbers processed by example program (presented later) for four different random generator implementations are presented in Excel screenshot below.




















HEADER FILE


Random generator template class is presented below. Note, that this implementation does not require any auxiliary libraries, since all the required stuff is already in C++11. For usability reasons, I decided to keep all the actual method implementations in header file (RandomGenerator.h).

#pragma once
#include <algorithm>
#include <chrono>
#include <cmath>
#include <functional>
#include <iostream>
#include <math.h>
#include <memory>
#include <random>
#include <string>
#include <vector>
//
namespace MikeJuniperhillRandomGeneratorTemplate
{
 template <typename Generator = std::mt19937, typename Distribution = std::normal_distribution<double>>
 /// <summary> 
 /// Template class for creating random number paths using
 /// Mersenne Twister as default uniform random generator and 
 /// Standard Normal (0.0, 1.0) as default probability distribution.
 /// </summary>  
 class RandomGenerator
 {
 public:
  /// <summary>
  /// Constructor for using default probability distribution 
  /// for transforming uniform random numbers.
  /// </summary>  
  RandomGenerator(const std::function<unsigned long(void)>& seeder)
   : seeder(seeder)
  {
   // construct lambda method for processing standard normal random number
   randomGenerator = [this](double x)-> double
   {
    x = distribution(uniformGenerator);
    return x;
   };
   // seed uniform random generator with a client-given algorithm
   uniformGenerator.seed(seeder());
  }
  /// <summary> 
  /// Constructor for using client-given probability distribution 
  /// for transforming uniform random numbers.
  /// </summary>  
  RandomGenerator(const std::function<unsigned long(void)>& seeder, const Distribution& distribution)
   // constructor delegation for initializing other required member data
   : RandomGenerator(seeder)
  {
   // assign client-given probability distribution
   this->distribution = distribution;
  }
  /// <summary> 
  /// Functor filling vector with random numbers mapped to chosen probability distribution.
  /// </summary>  
  void operator()(std::vector<double>& v)
  {
   std::transform(v.begin(), v.end(), v.begin(), randomGenerator);
  }
 private:
  std::function<unsigned long(void)> seeder;
  std::function<double(double)> randomGenerator;
  Generator uniformGenerator;
  Distribution distribution;
 };
}


TESTER FILE


Example program (tester.cpp) for testing random generator functionality is presented below. The process of generating (pseudo) random numbers from a probability distribution is easy and straightforward with this template wrapper class.

#include "RandomGenerator.h"
namespace MJ = MikeJuniperhillRandomGeneratorTemplate;
//
// printer for a random path
void Print(const std::string& message, const std::vector<double>& v)
{
 std::cout << message << std::endl;
 for (auto& element : v) std::cout << element << std::endl;
 std::cout << std::endl;
}
//
int main()
{
 // construct lambda method for seeding uniform random generator
 std::function<unsigned long(void)> seeder = 
  [](void) -> unsigned long { return static_cast<unsigned long>
  (std::chrono::steady_clock::now().time_since_epoch().count()); };
 //
 // create vector for a path to be filled with 20 random numbers
 // from desired probability distribution, using desired uniform random generator
 int nSteps = 20;
 std::vector<double> path(nSteps);
 //
 // path generator : mersenne twister, standard normal
 MJ::RandomGenerator<> gen_1(seeder);
 gen_1(path);
 Print("mt19937 : x~N(0.0, 1.0)", path);
 //
 // path generator : minst_rand, standard normal
 MJ::RandomGenerator<std::minstd_rand> gen_2(seeder);
 gen_2(path);
 Print("minstd_rand : x~N(0.0, 1.0)", path);
 //
 // path generator : mersenne twister, normal(112.5, 1984.0)
 std::normal_distribution<double> nd(112.5, 1984.0);
 MJ::RandomGenerator<> gen_3(seeder, nd);
 gen_3(path);
 Print("mt19937 : x~N(112.5, 1984.0)", path);
 //
 // path generator : minstd_rand, exponential(3.14)
 std::exponential_distribution<double> ed(3.14);
 MJ::RandomGenerator<std::minstd_rand, std::exponential_distribution<double>> gen_4(seeder, ed);
 gen_4(path);
 Print("minstd_rand : x~Exp(3.14)", path);
 //
 return 0;
}

Finally, thanks again for reading this blog.
-Mike

No comments:

Post a Comment