Il y a quelque temps, j'avais créé un ensemble de classes d'encapsulation pour inclure la plupart des générateurs de nombres aléatoires de la std, les moteurs, les types de graines et les distributions pour qu'ils fonctionnent ensemble de manière transparente. Vous êtes libre d'utiliser cette classe et de la modifier pour répondre à vos propres besoins si vous le souhaitez. Ici, il s'agit uniquement de la classe d'en-tête et toutes les fonctions sont déclarées comme statiques. Les constructeurs sont protégés par défaut. Vous ne pouvez pas créer une instance de ces classes. Il y a 2 classes : RandomEngine
y RandomDistribution
. Pour rendre la vie un peu plus facile après les deux classes, j'ai créé 2 typedefs
pour réduire le temps de frappe lors de leur utilisation, RE
& RD
respectivement. Il existe quelques ensembles de enums
dans ces classes, seul l'un d'entre eux est directement utilisé, les 2 autres sont là juste pour une référence visuelle mais l'utilisateur peut les utiliser si nécessaire. Voici les classes dans un fichier d'en-tête seulement.
RandomGenerator.h
#ifndef RANDOM_GENERATOR_H
#define RANDOM_GENERATOR_H
#include <limits>
#include <chrono>
#include <random>
// ----------------------------------------------------------------------------
// Class RandomEngine { typedef = RE }
class RandomEngine {
public:
using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock>;
// Used To Determine Which Seeding Process To Use
enum SeedType {
USE_CHRONO_CLOCK,
USE_RANDOM_DEVICE,
USE_SEED_VALUE,
USE_SEED_SEQ,
}; // SeedType
// This Enum Is Not In Use - It Is A Visual Reference Only; But If User Wants To
// Use It For Their Own Pupose They Are Free To Do So.
enum EngineType {
// Default Random Engine
DEFAULT_RANDOM_ENGINE,
// Linear Congruential Engines
MINSTD_RAND0,
MINSTD_RAND,
// Mersenne Twister Engines
MT19937,
MT19937_64,
// Subtract With Carry Engines
RANLUX24_BASE,
RANLUX48_BASE,
// Discard Block Engines
RANLUX24,
RANLUX48,
// Shuffle Order Engines
KNUTH_B,
}; // EngineType
protected:
RandomEngine() = default;
// Internal Helper Function
// ---------------------------------------------------------------------------
// getRandomDevice()
static std::random_device& getRandomDevice() {
static std::random_device device{};
return device;
} // getRandomDevice
public:
// ---------------------------------------------------------------------------
// getTimeNow()
static unsigned int getTimeNow() {
unsigned int now = static_cast<unsigned int>(Clock::now().time_since_epoch().count());
return now;
} // getTimeNow
// ---------------------------------------------------------------------------
// getDefaultRandomEngine()
static std::default_random_engine& getDefaultRandomEngine( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::default_random_engine engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getDefaultRandomEngine
// ---------------------------------------------------------------------------
// getMinStd_Rand0()
static std::minstd_rand0& getMinStd_Rand0( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::minstd_rand0 engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getMinStd_Rand0
// ---------------------------------------------------------------------------
// getMinStd_Rand()
static std::minstd_rand& getMinStd_Rand( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::minstd_rand engine{};
switch( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed(seq);
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getMinStd_Rand
// ---------------------------------------------------------------------------
// getMt19937()
static std::mt19937& getMt19937( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::mt19937 engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} //getMt19937
// ---------------------------------------------------------------------------
// getMt19937_64()
static std::mt19937_64& getMt19937_64( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::mt19937_64 engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getMt19937_64
// ---------------------------------------------------------------------------
// getRanLux24_base()
static std::ranlux24_base& getRanLux24_base( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::ranlux24_base engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getRanLux24_base
// ---------------------------------------------------------------------------
// getRanLux48_base()
static std::ranlux48_base& getRanLux48_base( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::ranlux48_base engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getRanLux48_base
// ---------------------------------------------------------------------------
// getRanLux24()
static std::ranlux24& getRanLux24( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::ranlux24 engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} // getRanLux24
// ---------------------------------------------------------------------------
// getRanLux48()
static std::ranlux48& getRanLux48( SeedType type, unsigned seedValue = 0, std::seed_seq& seq = std::seed_seq{} ) {
static std::ranlux48 engine{};
switch ( type ) {
case USE_CHRONO_CLOCK: {
engine.seed( getTimeNow() );
break;
}
case USE_SEED_VALUE: {
engine.seed( seedValue );
break;
}
case USE_SEED_SEQ: {
engine.seed( seq );
break;
}
default: {
engine.seed( getRandomDevice()() );
break;
}
}
return engine;
} //getRanLux48
private:
}; // RandomEngine
// ----------------------------------------------------------------------------
// Class - RandomDistrubtion { typedef = RD }
class RandomDistribution {
public:
// This Enum Is Not In Use - It Is A Visual Reference Only; But If User Wants To
// Use It For Their Own Pupose They Are Free To Do So.
enum DistributionType {
// Uniform Distributions
UNIFORM_INT,
UNIFORM_INT_DISTRIBUTION,
UNIFORM_REAL,
UNIFORM_REAL_DISTRIBUTION,
// GENERATE_CANONICAL, - This is a function template and not a class template use it directly form std:: <random> c++11
// Bernoulli Distributions
BERNOULLI_DISTRIBUTION,
BINOMAIL_DISTRIBUTION,
NEGATIVE_BINOMIAL_DISTRIBUTION,
GEOMETRIC_DISTRIBUTION,
// Poisson Distributions
POISSON_DISTRIBUTION,
EXPONENTIAL_DISTRIBUTION,
GAMMA_DISTRIBUTION,
WEIBULL_DISTRIBUTION,
EXTREME_VALUE_DISTRIBUTION,
// Normal Distributions
NORMAL_DISTRIBUTION,
LOGNORMAL_DISTRIBUTION,
CHI_SQUARED_DISTRIBUTION,
CAUCHY_DISTRIBUTION,
FISHER_F_DISTRIBUTION,
STUDENT_T_DISTRIBUTION,
// Sampling Distributions
DISCRETE_DISTRIBUTION,
PIECEWISE_CONSTANT_DISTRIBUTION,
PIECEWISE_LINEAR_DISTRIBUTION
}; // DistributionType
protected:
RandomDistribution() = default;
public:
// UNIFORM DISTRIBUTIONS
// ---------------------------------------------------------------------------
// getUniformIntDistribution()
template<class IntType = int>
static std::uniform_int_distribution<IntType>& getUniformIntDistribution( IntType lowerBound = 0, IntType upperBound = (std::numeric_limits<IntType>::max)() ) {
static std::uniform_int_distribution<IntType> dist( lowerBound, upperBound );
return dist;
} // getUniformIntDistribution
// ---------------------------------------------------------------------------
// getUniformRealDistribution()
template<class RealType = double>
static std::uniform_real_distribution<RealType>& getUniformRealDistribution( RealType lowerBound = 0.0, RealType upperBound = 1.0 ) {
static std::uniform_real_distribution<RealType> dist( lowerBound, upperBound );
return dist;
} // getUniformRealDistribution
// BERNOULLI DISTRIBUTIONS
// ---------------------------------------------------------------------------
// getBernoulliDistribution()
static std::bernoulli_distribution& getBernoulliDistribution( double probability = 0.5 ) {
static std::bernoulli_distribution dist( probability );
return dist;
} // getBernoulliDistribution
// ---------------------------------------------------------------------------
// getBinomialDistribution()
template<class IntType = int>
static std::binomial_distribution<IntType>& getBinomialDistribution( IntType numTrials = 1, double probability = 0.5 ) {
static std::binomial_distribution<IntType> dist( numTrials, probability );
return dist;
} // getBinomialDistribution
// ---------------------------------------------------------------------------
// getNegativeBinomialDistribution()
template<class IntType = int>
static std::negative_binomial_distribution<IntType>& getNegativeBinomialDistribution( IntType numTrialFailures = 1, double probability = 0.5 ) {
static std::negative_binomial_distribution<IntType> dist( numTrialFailures, probability );
return dist;
} // getNegativeBinomialDistribution
// ---------------------------------------------------------------------------
// getGeometricDistribution()
template<class IntType = int>
static std::geometric_distribution<IntType>& getGeometricDistribution( double probability = 0.5 ) {
static std::geometric_distribution<IntType> dist( probability );
return dist;
} // getGeometricDistribution
// POISSON DISTRIBUTIONS
// ---------------------------------------------------------------------------
// getPoissonDistribution()
template<class IntType = int>
static std::poisson_distribution<IntType>& getPoissonDistribution( double mean = 1.0 ) {
static std::poisson_distribution<IntType> dist( mean );
return dist;
} // getPoissonDistribution
// ---------------------------------------------------------------------------
// getExponentialDistribution()
template<class RealType = double>
static std::exponential_distribution<RealType>& getExponentialDistribution( RealType rate = 1.0 ) {
static std::exponential_distribution<RealType> dist( rate );
return dist;
} // getExponentialDistribution
// ---------------------------------------------------------------------------
// getGammDistribution()
template<class RealType = double>
static std::gamma_distribution<RealType>& getGammaDistribution( RealType alpha_shape = 1.0, RealType beta_scale = 1.0 ) {
static std::gamma_distribution<RealType> dist( alpha_shape, beta_scale );
return dist;
} // getGammaDistribution
// ---------------------------------------------------------------------------
// getWeibullDistribution()
template<class RealType = double>
static std::weibull_distribution<RealType>& getWeibullDistribution( RealType alpha_shape = 1.0, RealType beta_scale = 1.0 ) {
static std::weibull_distribution<RealType> dist( alpha_shape, beta_scale );
return dist;
} // getWeibullDistribution
// ---------------------------------------------------------------------------
// getExtremeValueDistribution()
template<class RealType = double>
static std::extreme_value_distribution<RealType>& getExtremeValueDistribution( RealType location = 0.0, RealType scale = 1.0 ) {
static std::extreme_value_distribution<RealType> dist( location, scale );
return dist;
} // getExtremeValueDistribution
// NORMAL DISTRIBUTIONS
// ---------------------------------------------------------------------------
// getNormalDistribution()
template<class RealType = double>
static std::normal_distribution<RealType>& getNormalDistribution( RealType mean = 0.0, RealType stddev = 1.0 ) {
static std::normal_distribution<RealType> dist( mean, stddev );
return dist;
} // getNormaDistribution
// ---------------------------------------------------------------------------
// getLogNormalDistribution()
template<class RealType = double>
static std::lognormal_distribution<RealType>& getLogNormalDistribution( RealType logScale = 0.0, RealType shape = 1.0 ) {
static std::lognormal_distribution<RealType> dist( logScale, shape );
return dist;
} // getLogNormalDistribution
// ---------------------------------------------------------------------------
// getChiSquaredDistribution()
template<class RealType = double>
static std::chi_squared_distribution<RealType>& getChiSquaredDistribution( RealType degreesOfFreedom = 1.0 ) {
static std::chi_squared_distribution<RealType> dist( degreesOfFreedom );
return dist;
} // getChiSquaredDistribution
// ---------------------------------------------------------------------------
// getCauchyDistribution()
template<class RealType = double>
static std::cauchy_distribution<RealType>& getCauchyDistribution( RealType location = 0.0, RealType scale = 1.0 ) {
static std::cauchy_distribution<RealType> dist( location, scale );
return dist;
} // getCauchyDistribution
// ---------------------------------------------------------------------------
// getFisherFDistribution() Both m,n are degress of freedom
template<class RealType = double>
static std::fisher_f_distribution<RealType>& getFisherFDistribution( RealType m = 1.0, RealType n = 1.0 ) {
static std::fisher_f_distribution<RealType> dist( m, n );
return dist;
} // getFisherFDistribution
// ---------------------------------------------------------------------------
// getStudentTDistribution()
template<class RealType = double>
static std::student_t_distribution<RealType>& getStudentTDistribution( RealType degreesOfFreedom = 1.0 ) {
static std::student_t_distribution<RealType> dist( degreesOfFreedom );
return dist;
} // getStudentTDistribution
// SAMPLING DISTRIBUTIONS
// ---------------------------------------------------------------------------
// getDiscreteDistribution()
template<class IntType = int>
static std::discrete_distribution<IntType>& getDiscreteDistribution() {
static std::discrete_distribution<IntType> dist;
return dist;
} // getDiscreteDistribution
// ---------------------------------------------------------------------------
// getDiscreteDistribution()
template<class IntType = int, class InputIt>
static std::discrete_distribution<IntType>& getDiscreteDistribution( InputIt first, InputIt last ) {
static std::discrete_distribution<IntType> dist( first, last );
return dist;
} // getDiscreteDistribution
// ---------------------------------------------------------------------------
// getDiscreteDistribution()
template<class IntType = int>
static std::discrete_distribution<IntType>& getDiscreteDistribution( std::initializer_list<double> weights ) {
static std::discrete_distribution<IntType> dist( weights );
return dist;
} // getDiscreteDistribution
// ---------------------------------------------------------------------------
// getDiscreteDistribution()
template<class IntType = int, class UnaryOperation>
static std::discrete_distribution<IntType>& getDiscreteDistribution( std::size_t count, double xmin, double xmax, UnaryOperation unary_op ) {
static std::discrete_distribution<IntType> dist( count, xmin, xmax, unary_op );
return dist;
} // getDiscreteDistribution
// ---------------------------------------------------------------------------
// getPiecewiseConstantDistribution()
template<class RealType = double>
static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution() {
static std::piecewise_constant_distribution<RealType> dist;
return dist;
} // getPiecewiseConstantDistribution
// ---------------------------------------------------------------------------
// getPiecewiseConstantDistribution()
template<class RealType = double, class InputIt1, class InputIt2>
static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution( InputIt1 first_i, InputIt1 last_i, InputIt2 first_w ) {
static std::piecewise_constant_distribution<RealType> dist( first_i, last_i, first_w );
return dist;
} // getPiecewiseConstantDistribution
// ---------------------------------------------------------------------------
// getPiecewiseConstantDistribution()
template<class RealType = double, class UnaryOperation>
static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution( std::initializer_list<RealType> bl, UnaryOperation fw ) {
static std::piecewise_constant_distribution<RealType> dist( bl, fw );
return dist;
} // getPiecewiseConstantDistribution
// ---------------------------------------------------------------------------
// getPiecewiseConstantDistribution()
template<class RealType = double, class UnaryOperation>
static std::piecewise_constant_distribution<RealType>& getPiecewiseConstantDistribution( std::size_t nw, RealType xmin, RealType xmax, UnaryOperation fw ) {
static std::piecewise_constant_distribution<RealType> dist( nw, xmin, xmax, fw );
return dist;
} // getPiecewiseConstantDistribution
// ---------------------------------------------------------------------------
// getPiecewiseLinearDistribution()
template<class RealType = double>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution() {
static std::piecewise_linear_distribution<RealType> dist;
return dist;
} // getPiecewiseLinearDistribution
// ---------------------------------------------------------------------------
// getPiecewiseLinearDistribution()
template<class RealType = double, class InputIt1, class InputIt2>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution( InputIt1 first_i, InputIt1 last_i, InputIt2 first_w ) {
static std::piecewise_linear_distribution<RealType> dist( first_i, last_i, first_w );
return dist;
} // getPiecewiseLinearDistribution
// ---------------------------------------------------------------------------
// getPiecewiseLinearDistribution()
template<class RealType = double, class UnaryOperation>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution( std::initializer_list<RealType> bl, UnaryOperation fw ) {
static std::piecewise_linear_distribution<RealType> dist( bl, fw );
return dist;
} // getPiecewiseLinearDistribution
// ---------------------------------------------------------------------------
// getPiecewiseLinearDistribution()
template<class RealType = double, class UnaryOperation>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution( std::size_t nw, RealType xmin, RealType xmax, UnaryOperation fw ) {
static std::piecewise_linear_distribution<RealType> dist( nw, xmin, xmax, fw );
return dist;
} // getPiecewiseLinearDistribution
}; // RandomDistribution
typedef RandomEngine RE;
typedef RandomDistribution RD;
#endif // !RANDOM_GENERATOR_H
Et on utiliserait cette classe comme telle, comme on peut le voir dans ces quelques exemples ci-dessous.
main.cpp
#include <sstream>
#include <iostream>
#include "RandomGenerator.h"
int main() {
std::ostringstream strStream;
strStream << "Random number generated between [0.0, 1.0] \nusing mersenne & chrono clock for seeding:\n";
std::cout << strStream.str();
std::uniform_real_distribution<double> urd = RD::getUniformRealDistribution<double>( 0.0, 1.0 );
for ( unsigned i = 1; i <= 50; i++ ) {
std::ostringstream strStream;
double val = urd( RE::getMt19937( RE::SeedType::USE_CHRONO_CLOCK, 12 ) );
strStream << i << " : " << val << "\n";
std::cout << strStream.str();
}
std::cout << std::endl;
strStream.clear();
//std::ostringstream strStream;
strStream << "Random number generated Between [1,9] using default random engine & uniform int distribution is: " << std::endl;
std::cout << strStream.str();
std::uniform_int_distribution<unsigned> uid = RD::getUniformIntDistribution<unsigned>( 1, 9 );
// std::uniform_int_distribution<unsigned> uid( 1, 9 );
for ( unsigned int i = 1; i < 101; i++ ) {
std::ostringstream strStream;
unsigned val = uid( RE::getDefaultRandomEngine( RE::SeedType::USE_CHRONO_CLOCK, 14 ) );
strStream << i << " : " << val << std::endl;
std::cout << strStream.str();
}
std::cout << std::endl;
for ( unsigned int i = 1; i < 101; i++ ) {
std::ostringstream strStream;
// Using the same distribution above but reseeding it with a different type of seeding method.
unsigned val = uid( RE::getDefaultRandomEngine( RE::SeedType::USE_RANDOM_DEVICE ) );
strStream << i << " : " << val << std::endl;
std::cout << strStream.str();
}
std::cout << "\nPress any key and enter to quit." << std::endl;
char q;
std::cin >> q;
return 0;
}
Ces classes déclareront, définiront et appelleront automatiquement les générateurs et les distributions en utilisant les méthodes statiques avec les paramètres dont elles ont besoin. Si vous avez besoin qu'elles soient locales, il ne devrait pas y avoir de problème pour les modifier selon vos besoins.
En général, j'utilise cette méthode pour inclure cet en-tête dans la classe qui a besoin d'une distribution aléatoire. J'ai ensuite une variable membre du type de distribution dont j'ai besoin et je la règle en utilisant le générateur souhaité et le mécanisme de type d'ensemencement requis.
Si vous avez des questions, n'hésitez pas à les poser.