Boost random number generator
This code is adapted from the boost manual at http://www.boost.org/doc/libs/1_42_0/libs/random/index.html:
#include <iostream>
#include "boost/random.hpp"
#include "boost/generator_iterator.hpp"
using namespace std;
int main() {
typedef boost::mt19937 RNGType;
RNGType rng;
boost::uniform_int<> one_to_six( 1, 6 );
boost::variate_generator< RNGType, boost::uniform_int<> >
dice(rng, one_to_six);
for ( int i = 0; i < 6; i++ ) {
int n = dice();
cout << n << endl;
}
}
To explain the bits:
mt19937
is the mersenne twister generator,which generates the raw random numbers. A typedef is used here so you can easily change random number generator type.rng
is an instance of the twister generator.one_to_six
is an instance of a distribution. This specifies the numbers we want to generate and the distribution they follow. Here we want 1 to 6, distributed evenly.dice
is the thing that takes the raw numbers and the distribution, and creates for us the numbers we actually want.dice()
is a call tooperator()
for thedice
object, which gets the next random number following the distribution, simulating a random six-sided dice throw.
As it stands, this code produces the same sequence of dice throws each time. You can randomise the generator in its constructor:
RNGType rng( time(0) );
or by using its seed() member.
How do I use Boost Random
the random number for my case must be double not just integer...
So, you use a real number distribution.
I'm not this kind of "getting started" is the best fit for StackOverflow, but I'll give you this quick hints:
In your Ubuntu virtual box:
sudo apt-get install libboost-all-dev
mkdir -pv ~/myproject
cd ~/myproject
Create a file using your favourite editor. If you have none, gedit main.cpp
or nano main.cpp
is a start:
#include <boost/random.hpp>
#include <iostream>
int main() {
boost::random::mt19937 rng;
boost::random::uniform_real_distribution<double> gen(0.0, 1.0);
for (int i = 0; i < 10; ++i) {
std::cout << gen(rng) << "\n";
}
}
Now compile it using
g++ -O2 -Wall -Wextra -pedantic main.cpp -o demo
The program is now ready to run: Live On Coliru
./demo
Printing
0.814724
0.135477
0.905792
0.835009
0.126987
0.968868
0.913376
0.221034
0.632359
0.308167
Seeding && Non-Header Only Libraries
The above works because the Boost Random library is mostly header only. What if you wanted to use the random_device
implementation to seed the random generator?
Live On Coliru
#include <boost/random.hpp>
#include <boost/random/random_device.hpp>
#include <iostream>
int main() {
boost::random::random_device seeder;
boost::random::mt19937 rng(seeder());
boost::random::uniform_real_distribution<double> gen(0.0, 1.0);
for (int i = 0; i < 10; ++i) {
std::cout << gen(rng) << "\n";
}
}
Now you'll have to link as well: Compiling with
g++ -O2 -Wall -Wextra -pedantic main.cpp -o demo -lboost_random
Now the output will be different each run.
BONUS: Standard Library instead of Boost
You don't need Boost here at all:
Live On Coliru
#include <random>
#include <iostream>
int main() {
std::random_device seeder;
std::mt19937 rng(seeder());
std::uniform_real_distribution<double> gen(0.0, 1.0);
for (int i = 0; i < 10; ++i) {
std::cout << gen(rng) << "\n";
}
}
Compile with
g++ -std=c++11 -O2 -Wall -Wextra -pedantic main.cpp -o demo
And run it again with ./demo
BONUS
Showing a whole gamut of distributions that have mean=0 and stddev=1:
Live On Coliru
#include <random>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <boost/serialization/array_wrapper.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
namespace ba = boost::accumulators;
using Accum = ba::accumulator_set<double, ba::stats<ba::tag::variance, ba::tag::mean> >;
using Clock = std::chrono::high_resolution_clock;
using namespace std::chrono_literals;
static double identity(double d) { return d; }
template <typename Prng, typename Dist, typename F = double(double), size_t N = (1ull << 22)>
void test(Prng& rng, Dist dist, F f = &identity) {
Accum accum;
auto s = Clock::now();
for (size_t i = 0; i<N; ++i)
accum(f(dist(rng)));
std::cout
<< std::setw(34) << typeid(Dist).name()
<< ":\t" << ba::mean(accum)
<< " stddev: " << sqrt(ba::variance(accum))
<< " N=" << N
<< " in " << ((Clock::now()-s)/1.s) << "s"
<< std::endl;
}
int main() {
std::mt19937 rng(std::random_device{}());
auto shift = [](double shift) { return [=](double v) { return v + shift; }; };
auto scale = [](double scale) { return [=](double v) { return v * scale; }; };
std::cout << std::fixed << std::showpos;
test(rng, std::uniform_real_distribution<double>(-sqrt(3), sqrt(3)));
test(rng, std::weibull_distribution<double>(), shift(-1));
test(rng, std::exponential_distribution<double>(), shift(-1));
test(rng, std::normal_distribution<double>());
test(rng, std::lognormal_distribution<double>(0, log(0.5)), shift(-exp(pow(log(0.5),2)/2)));
test(rng, std::chi_squared_distribution<double>(0.5), shift(-0.5));
{
auto sigma = sqrt(6)/M_PI;
static constexpr double ec = 0.57721566490153286060;
test(rng, std::extreme_value_distribution<double>(-sigma*ec, sigma));
}
test(rng, std::fisher_f_distribution<double>(48, 8), shift(-(8.0/6.0)));
test(rng, std::student_t_distribution<double>(4), scale(sqrt(0.5)));
test(rng, std::student_t_distribution<double>(4), scale(sqrt(0.5)));
}
Prints
St25uniform_real_distributionIdE: +0.000375 stddev: +1.000056 N=4194304 in +0.169681s
St20weibull_distributionIdE: +0.001030 stddev: +1.000518 N=4194304 in +0.385036s
St24exponential_distributionIdE: -0.000360 stddev: +1.000343 N=4194304 in +0.389443s
St19normal_distributionIdE: -0.000133 stddev: +1.000330 N=4194304 in +0.390235s
St22lognormal_distributionIdE: +0.000887 stddev: +1.000372 N=4194304 in +0.521975s
St24chi_squared_distributionIdE: -0.000092 stddev: +0.999695 N=4194304 in +1.233835s
St26extreme_value_distributionIdE: -0.000381 stddev: +1.000242 N=4194304 in +0.611973s
St21fisher_f_distributionIdE: -0.000073 stddev: +1.001588 N=4194304 in +1.326189s
St22student_t_distributionIdE: +0.000957 stddev: +0.998087 N=4194304 in +1.080468s
St22student_t_distributionIdE: +0.000677 stddev: +0.998786 N=4194304 in +1.079066s
C++ boost random number generator set seed for multiple instances
Like Christoph commented, if you copy the state of the generator engine, you'll have two engines with the same state.
So seed the engines after the copy:
template<class... Args>
Distribution(Args... args):
variate_generator(random_generator,Type(args...)) {
boost::random::random_device dev;
variate_generator.engine().seed(dev);
}
Note how seeding from random_device
is vastly preferable. This makes sure the seed is itself random and also that the entire state of the engine is seeded.
If you don't want to link to Boost Random, you can use a single seed value again:
template<class... Args>
Distribution(Args... args):
variate_generator(random_generator,Type(args...)) {
std::random_device dev;
variate_generator.engine().seed(dev());
}
Other Issues
When you do
normal_random_generator = {mu, sigma_};
you're REPLACING your global Distribution
instance, and setting mu
to the value you get from main. Since you (ab)use rand()
there, mu
will just be some completely un-random and largish value. On my system it's always
1804289383
846930886
1681692777
1714636915
1957747793
424238335
719885386
1649760492
596516649
1189641421
Your distribution's sigma is pretty small in comparison, therefore your generated values will be close to the original, and the scientific formatting of the number will hide any difference:
!!!Begin!!!
starting values: individual a = 0.4 individual b = 0.4
A B
1.80429e+09 1.80429e+09
2.65122e+09 2.65122e+09
4.33291e+09 4.33291e+09
6.04755e+09 6.04755e+09
8.0053e+09 8.0053e+09
8.42954e+09 8.42954e+09
9.14942e+09 9.14942e+09
1.07992e+10 1.07992e+10
1.13957e+10 1.13957e+10
1.25853e+10 1.25853e+10
finished
That looks as if both columns have the same values. However, they're basically just the output of rand()
with minor variation. Adding
std::cout << std::fixed;
Shows that there ARE differences:
!!!Begin!!!
starting values: individual a = 0.4 individual b = 0.4
A B
1804289383.532134 1804289383.306165
2651220269.054946 2651220269.827112
4332913046.416999 4332913046.791281
6047549960.973747 6047549961.979666
8005297753.938927 8005297755.381466
8429536088.122741 8429536090.737263
9149421474.458202 9149421477.268963
10799181966.514246 10799181969.109875
11395698614.754076 11395698617.892900
12585340035.563337 12585340038.882833
finished
All in all, I'd suggest
- not using
rand()
and/or picking a more suitable range formean
Also I suggest never using global variables. With the fact that you create a new instance of
Normal
every time here:normal_random_generator = {mu, sigma_};
I don't see what value there could possibly be in overwriting a global variable with that instance. It just makes it less efficient. So, this is strictly equivalent and more efficient:
void move_bias_random_walk(double mu) {
Normal nrg {mu, sigma_};
distance_ += nrg.random();
}Understand your distribution's Sigma, so you can predict the variance of the numbers to expect.
Fixed Code #1
Live On Coliru
// C/C++ standard library
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/lognormal_distribution.hpp>
#include <boost/random/random_device.hpp>
/**
* The mt11213b generator is fast and has a reasonable cycle length
* See http://www.boost.org/doc/libs/1_60_0/doc/html/boost_random/reference.html#boost_random.reference.generators
*/
typedef boost::mt11213b Engine;
boost::random::random_device random_device;
template<
class Type
> struct Distribution {
boost::variate_generator<Engine, Type> variate_generator;
template<class... Args>
Distribution(Args... args):
variate_generator(Engine(random_device()), Type(args...)) {
//variate_generator.engine().seed(random_device);
//std::cout << "ctor test: " << variate_generator.engine()() << "\n";
}
double random(void) {
double v = variate_generator();
//std::cout << "debug: " << v << "\n";
return v;
}
};
typedef Distribution< boost::normal_distribution<> > Normal;
// Class Individual
class Individual {
public:
Individual() { } // constructor initialise value
virtual ~Individual() = default;
// an accessor to pass information back
void move_bias_random_walk(double mu) {
Normal nrg {mu, sigma_};
distance_ += nrg.random();
}
// An accessor for the distance object
double get_distance() {
return distance_;
}
private:
//containers
double distance_ = 0.4;
double sigma_ = 0.4;
};
int main() {
std::cout << std::fixed;
std::cout << "!!!Begin!!!" << std::endl;
// Initialise two individuals in this case but there could be thousands
Individual individual_a;
Individual individual_b;
std::cout << "starting values: individual a = " << individual_a.get_distance() << " individual b = " << individual_b.get_distance() << std::endl;
// Do 10 jumps with the same mean for each individual and see where they end up each time
std::cout << "A\tB" << std::endl;
for (auto i = 1; i <= 10; ++i) {
double mean = rand()%10;
//std::cout << "mean: " << mean << "\n";
individual_a.move_bias_random_walk(mean);
individual_b.move_bias_random_walk(mean);
std::cout << individual_a.get_distance() << "\t" << individual_b.get_distance() << std::endl;
}
std::cout << "finished" << std::endl;
}
Prints
!!!Begin!!!
starting values: individual a = 0.400000 individual b = 0.400000
A B
3.186589 3.754065
9.341219 8.984621
17.078740 16.054461
21.787808 21.412336
24.896861 24.272279
29.801920 29.090233
36.134987 35.568845
38.228595 37.365732
46.833353 46.410176
47.573564 47.194575
finished
Simplifying: Demo #2
The following is exactly equivalent but way more efficient:
Live On Coliru
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/random_device.hpp>
#include <iostream>
/**
* The mt11213b generator is fast and has a reasonable cycle length
* See http://www.boost.org/doc/libs/1_60_0/doc/html/boost_random/reference.html#boost_random.reference.generators
*/
typedef boost::mt11213b Engine;
template <typename Distribution>
class Individual {
public:
Individual(Engine& engine) : engine_(engine) { }
// an accessor to pass information back
void move_bias_random_walk(double mu) {
Distribution dist { mu, sigma_ };
distance_ += dist(engine_);
}
// An accessor for the distance object
double get_distance() {
return distance_;
}
private:
Engine& engine_;
//containers
double distance_ = 0.4;
double sigma_ = 0.4;
};
int main() {
boost::random::random_device device;
Engine engine(device);
std::cout << std::fixed;
std::cout << "!!!Begin!!!" << std::endl;
// Initialise two individuals in this case but there could be thousands
Individual<boost::normal_distribution<> > individual_a(engine);
Individual<boost::normal_distribution<> > individual_b(engine);
std::cout << "starting values: individual a = " << individual_a.get_distance() << " individual b = " << individual_b.get_distance() << std::endl;
// Do 10 jumps with the same mean for each individual and see where they end up each time
std::cout << "A\tB" << std::endl;
for (auto i = 1; i <= 10; ++i) {
double mean = rand()%10;
individual_a.move_bias_random_walk(mean);
individual_b.move_bias_random_walk(mean);
std::cout << individual_a.get_distance() << "\t" << individual_b.get_distance() << std::endl;
}
std::cout << "finished" << std::endl;
}
Note
- it shares the
Engine
instance as you desire - it does not use global variables (or, worse, re-assign them!)
- otherwise as exactly the same behaviour but in much less code
Using boost to generate random numbers between 1 and 9999
Did you try googling for "boost random number" first? Here's the relevant part of their documentation generating boost random numbers in a range
You want something like this:
#include <time.h>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
std::time(0) gen;
int random_number(start, end) {
boost::random::uniform_int_distribution<> dist(start, end);
return dist(gen);
}
edit: this related question probably will answer most of your questions: why does boost::random return the same number each time?
Encapsulated Random number generator in C++-11 using boost
boost::variate_generator
takes its constructor arguments by value, so when you pass it a default constructed mt19937
in the mem-initializer list it makes a copy of it. Seeding the engine within the body of the constructor will have no effect on that copy. To fix this, change the template argument type to a reference.
typedef boost::variate_generator<ENG&,DIST> GEN;
// ^
Next, I don't think you want a normal_distribution
, its constructor arguments are not the minimum and maximum of the range of values produced by the distribution, but the mean and standard deviation of the distribution.
The distribution you're probably looking for is uniform_real_distribution
.
typedef boost::random::uniform_real_distribution<> DIST;
After making these fixes, your code should behave as you want it to.
Live demo
With a C++11 compiler you don't even need Boost to do this, the <random>
header provides all the components you need. The only significant difference is the lack of a variate_generator
class, instead you invoke the distribution object using the engine as the argument when you want to generate a random number.
#include <random>
typedef std::mt19937 ENG; // Mersenne Twister
typedef std::uniform_real_distribution<> DIST; // Uniform Distribution
class RNG {
private:
ENG eng;
DIST dist;
public:
DIST::result_type gen() { return dist(eng); }
RNG(double min,double max,int seed)
: dist(min,max)
{eng.seed(seed); }
};
Live demo
Random numbers, C++11 vs Boost
That’s pretty scary.
Let’s have a look:
boost::bernoulli_distribution<>
if(_p == RealType(0))
return false;
else
return RealType(eng()-(eng.min)()) <= _p * RealType((eng.max)()-(eng.min)());
std::bernoulli_distribution
__detail::_Adaptor<_UniformRandomNumberGenerator, double> __aurng(__urng);
if ((__aurng() - __aurng.min()) < __p.p() * (__aurng.max() - __aurng.min()))
return true;
return false;
Both versions invoke the engine and check if the output lies in a portion of the range of values proportional to the given probability.
The big difference is, that the gcc version calls the functions of a helper class _Adaptor
.
This class’ min
and max
functions return 0
and 1
respectively and operator()
then calls std::generate_canonical
with the given URNG to obtain a value between 0
and 1
.
std::generate_canonical
is a 20 line function with a loop – which will never iteratate more than once in this case, but it adds complexity.
Apart from that, boost uses the param_type
only in the constructor of the distribution, but then saves _p
as a double
member, whereas gcc has a param_type
member and has to “get” the value of it.
This all comes together and the compiler fails in optimizing.
Clang chokes even more on it.
If you hammer hard enough you can even get std::mt19937
and boost::mt19937
en par for gcc.
It would be nice to test libc++ too, maybe i’ll add that later.
tested versions: boost 1.55.0, libstdc++ headers of gcc 4.8.2
line numbers on request^^
Incorporating boost random number generator as a class variable
boost::variate_generator
has no default constructor, so you need to use your constructor's initialization list:
FR::FR()
: dist(0,1), gen(eng,dist)
{}
Consistent random number generation accross platforms with boost::random
it seems boost::random does not guarantee that you get the same sequence of numbers for certain seeds with different versions boost.
E.g. in version 1.56 they have changed the algorithm to generate normal distributed random numbers from the Box-Muller method to the Ziggurat method:
https://github.com/boostorg/random/commit/f0ec97ba36c05ef00f2d29dcf66094e3f4abdcde
This method is faster but also produces different number sequences.
Similar changes have probably been done to the other distributions. The uniform distribution still produces the same result as that is typically the output of the base rng which is a mersenne twister 19937 by default.
Related Topics
Intrinsics for Cpuid Like Informations
What Are the Issues with a Vector-Of-Vectors
Why Is This Cin Reading Jammed
Why Is This Code Trying to Call the Copy Constructor
Access Array Beyond the Limit in C and C++
Does Moving Leave the Object in a Usable State
Changing the Value of Const Variable in C++
C++: Construction and Initialization Order Guarantees
What Are the Distinctions Between the Various Symbols (*,&, etc) Combined with Parameters
C++ Return Value Created Before or After Auto Var Destruction
What Is the Status of N2965 - Std::Bases and Std::Direct_Bases
Constant References with Typedef and Templates in C++
Are Multiple Mutations of the Same Variable Within Initializer Lists Undefined Behavior Pre C++11
How to Cout a Float Number with N Decimal Places
Why Does -Int_Min = Int_Min in a Signed, Two's Complement Representation