Hello again! In this video, we are going to look at how to get random numbers in modern C++.
C++11 introduced a completely different way of working with random numbers, which is based around
classes.
These are defined in the header.
There are random number engine classes, which will generate random numbers.
There are distribution classes which will re-scale their input.
And there is also random_device, which you can use for seeding a random number engine.
These are all implemented as functors, with function call operators.
The constructor of the random number engine will generate a sequence of pseudo-random numbers. When
we call the function call operator the random number engine will return the next number from the sequence.
So here we are creating an object, default_random_engine, so that will generate the sequence of random
numbers. And then we call the function call operator. And, each time, that will return the next number in
the sequence.
So there we are, five random integers.
A distribution class is also implemented as a functor.
There will be an example, in a minute!
The constructor takes the range as arguments.
So if we put 1 and 100, then the range would be between 1 and 100, inclusive.
The function call operator of the distribution takes a function object as argument.
So typically that would be the random number engine.
Then that will call the function called operator on the random number engine.
Then that return value will be re-scaled, and fitted into the distribution.
And by the way, distributions are general purpose.
They are not just for random numbers.
If you have any numerical data sequence, you can write a functor which will return the next element in
the function
call operator.
And you can use that with a distribution.
So that is very useful, if you are writing applications which involve statistics.
The two main random number engine classes are: default_random_engine, which is implementation-defined.
It can just be a wrapper around the rand() implementation.
And the other one is mt19937.
Which is a catchy name!
This is the so-called "Mersenne Twister" algorithm, which has a period of 2 to the power of 19937
minus 1. As opposed to rand() implementations, which may only have two to the 16 minus
one.
This is very fast
at generating numbers.
It is very hard to guess the next number, so it is almost crypto secure.
It has a lot of state, so it takes a long time to initialize, and also to copy it.
And for most applications, this is the best general choice.
There are a wide range of distribution types.
We can have Bernoulli, normal, Poisson and so on.
Usually when we are dealing with random numbers, we want them to be uniformly distributed, which means
that all the values in the range are equally likely. For that,
we use uniform_int_distribution for integers. And we need to give the type.
The constructor will take the range of values.
And there is also a uniform_real_distribution for floating-point numbers.
So let's look at an example.
So we're doing basically the same code we had before, but this time we're using the Mersenne twister as
the engine.
And we are also going to use a uniform_int_distribution to re-scale the values.
We are going to re-scale them to be ints between 0 and 10. And then we call the distribution's function
call operator.
This will call the engine's function call operator to get the random numbers, and then it is going
to re-scale them. And then we do the same thing again, but with doubles.
So there we are, we have 5 random instances between 0 and 10, and 5 random floating-point numbers
between 0 and 10.
And the final class is random_device, this will generate true random numbers using hardware.
This is based on the system's entropy data.
So this is things like process ID, CPU temperature and things like that, which will scramble together,
but only if it is supported.
If you have a system which does not produce entropy data, then it will fall back to using a pseudo-random
number.
That is also the case if you are using GNU C++, which does not support entropy data, even if the system
provides it.
And also, the data depends on things which are happening on the system. If nothing happens on the
system, then all the entropy gets used up. And you have to wait until some more entropy becomes available.
So this can be very slow, but if it is fully implemented, then it is actually completely crypto
secure.
random_device is implemented as a functor again, so we just call the function call operator to
get the next random number.
Because it has these performance limitations,
it is not really suitable for using as a generator of random numbers, in large quantities.
The idea is that you would use the random_device to generate a seed.
So for example, you would create an object of an engine, and then you would pass a call to the random device
as argument.
So this would return a true random number, and then that will seat the random number engine.
So, some advice. I would recommend using mt19937, unless you have a particular
reason to use one of the others. If you are going to use random_device, check the documentation to
make sure it does actually do what it should do.
It does actually use hardware random numbers. When you create objects of the engine and the distribution,
make them static, because creating and copying these objects is fairly time consuming.
If you have them as local variables in a function, then every time you call the function, the engine
has to be initialized, which will take time.
And also, it will restart the sequence.
So you can end up getting the same random numbers every time you call the function!
And besides, you are probably only going to need one object per program anyway.
And finally, a health warning.
This is all aimed at people writing general-purpose applications. Random numbers are a bit like floating-
point numbers, in that they seem fairly straightforward. When you examine them in detail,
there is all sorts of hidden problems and scope for things to go wrong.
So if you are doing something really important, you do need to know what you are doing.
So bear that in mind.
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!