In general, any random number generator needs to be instantiated as an object before use. The resulting object can be called like a function without parameters because it overloads operator(). Every call will then lead to a new random number. It is that simple.
In this section, we wrote a program that is much more complex than that in order to get a bit more information about random number generators. Please play around with the resulting program by launching it with different command-line arguments and realize the following facts:
- The more samples we take, the more equal our partition counters appear.
- The inequality of the partition counters wildly differs between individual engines.
- For a large number of samples, it becomes apparent that the performance of the individual random engines differs.
- Run the program with a low amount of samples multiple times. The distribution patterns look the same all the time--the random engines produce the same random number sequences repeatedly, which means they are not random at all. Such engines are called deterministic because their random numbers can be predicted. The only exception is std::random_device.
As we can see, there are a few characteristics to consider. For most standard applications, std::default_random_engine will be completely sufficient. Experts of cryptography or similarly security-sensitive topics will choose wisely between the engines they use, but for us average programmers, this is not too important when we write apps with some randomness.
We should carry home the following three facts from this recipe:
- Usually, std::default_random_engine is a good default choice for the average application.
- If we really need non-deterministic random numbers, std::random_device provides us such.
- We can feed the constructor of any random engine with a real random number from std::random_device (or maybe a timestamp from the system clock), in order to make it produce different random numbers each time. This is called seeding.