What's New in Qt 5.10: QRandomGenerator
Computer software sometimes has a requirement for generating random numbers. Applications include games, simulations, cryptocurrency, and security software. Generating true random numbers is surprisingly difficult, and many applications use a series of pseudorandom numbers, sometimes seeded with an initial value that is not constant, such as user input or the current time or date.
The ISO standards for the C and C++ programming languages and other standards, such as POSIX, define standard random library functions. The most well-known are probably the rand() and srand() functions.
Since Qt version 4.2, cross-platform qrand() and qsrand() functions have been provided which are thread-safe and generate pseudorandom sequences of integers.
Modern computing operating systems such as Linux now provide more sophisticated random number generation facilities, sometimes making use of hardware random generator (HWRNG) support at the chip level. The BCM2837 SOC used by the Raspberry Pi, for example, has hardware random number generator support(1)(2). On Linux and other Unix-like operating systems, random generators are typically exposed as device files such as /dev/random, /dev/urandom, and /dev/hwrng(3).
QRandomGenerator
Qt 5.10 provides new classes for interfacing with OS level random number generators. It can generate various sizes of random data, coming from a variety of generator sources.
The Qt documentation(4) is, as usual, very complete and is probably the best way to understand how to use the modules. Rather than repeat similar information here, I thought I would just present some code examples. The complete buildable source for this first example can be found at the link(5) listed at the end of the blog post under References.
To use the module, include the header file <QRandomGenerator>. An additional class, QRandomGenerator64 has some more facilities for generating 64-bit data, if needed.
#include <QRandomGenerator>
#include <QRandomGenerator64>
Let's create some variables of different sized data for our example code:
int main()
{
quint32 value32;
quint64 value64;
int valueInt;
double valueDouble;
QRandomGenerator::result_type result;
The QRandomGenerator class provides a number of different constructors, optionally allowing you to seed them with an initial value:
// Constructors
QRandomGenerator gen1 = QRandomGenerator(); // Default, seeded with value of 1.
QRandomGenerator gen2 = QRandomGenerator(0x1234); // Default, seeded with value of 0x1234.
const quint32 array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
QRandomGenerator gen3 = QRandomGenerator(&array[0], &array[9]); // Generate a number of values.
QRandomGenerator gen4 = QRandomGenerator(gen1); // Copying another generator.
QRandomGenerator gen5 = QRandomGenerator::securelySeeded(); // Securely seeded generator.
QRandomGenerator *gen6 = QRandomGenerator::system(); // System generator.
QRandomGenerator *gen7 = QRandomGenerator::global(); // Global generator.
A number of methods allow generating random numbers of different sizes, potentially bounded to a range:
// Bounded values
valueDouble = gen1.bounded(100.0);
qDebug() << "Random double from 0 to 100:" << valueDouble;
value32 = gen1.bounded(100);
qDebug() << "Random 32-bit from 0 to 100:" << value32;
valueInt = gen1.bounded(100);
qDebug() << "Random int from 0 to 100:" << valueInt;
value32 = gen1.bounded(50, 100);
qDebug() << "Random 32-bit from 50 to 100:" << value32;
valueInt = gen1.bounded(50, 100);
qDebug() << "Random int from 50 to 100:" << valueInt;
Some overloaded methods named generate() can efficiently return 32 and 64-bit integers or double precision floating point values:
// Generate methods
value32 = gen5.generate();
qDebug() << "Random 32-bit:" << value32;
value64 = gen5.generate64();
qDebug() << "Random 64-bit:" << value64;
valueDouble = gen5.generateDouble();
qDebug() << "Random double:" << valueDouble;
You can also fill a container with random data:
// fillRange
QVector<quint32> vector;
vector.resize(10);
gen6->fillRange(vector.data(), vector.size());
qDebug() << "Random vector of" << vector.size() << "32-bit elements:" << vector;
The C++ functional call operator is also defined for a generator:
// Operator()
result = gen5();
qDebug() << "operator() returned" << result;
As well as seeding a generator when it is created, you can do so at any time using the seed() method. It can be a single value or even a seed sequence as defined by the C++11 standard:
// Seeding
gen2.seed(); // Default seed is 1
gen2.seed(1234);
std::seed_seq seq{1,2,3,4,5}; // C++11 standard seed sequence
gen2.seed(seq);
Methods are provided to return the range of values that a generator produces:
// Min/max
result = gen3.min();
qDebug() << "Min value:" << result;
result = gen4.max();
qDebug() << "Max value:" << result;
You can also tell a generator to discard a number of entries in the random sequence:
// Discard next 10 values
gen7->discard(10);
Comparison and assignment operators are defined for generators:
// Comparison
if (gen1 == gen2)
qDebug() << "gen1 and gen2 are the same";
if (gen1 != gen2)
qDebug() << "gen1 and gen2 are different";
// Assignment
gen2 = gen1;
if (gen1 == gen2)
qDebug() << "gen1 and gen2 are the same";
Finally, the QRandomGenerator64 class generates 64-bit integers by default:
// QRandomGenerator64
QRandomGenerator64 gen8 = QRandomGenerator64();
result = gen8();
qDebug() << "operator() returned" << result;
Here is sample output I saw from one run of the example program:
Random double from 0 to 100: 55.7944
Random 32-bit from 0 to 100: 70
Random int from 0 to 100: 69
Random 32-bit from 50 to 100: 82
Random int from 50 to 100: 53
Random 32-bit: 1439440802
Random 64-bit: 4502324789141989589
Random double: 0.225577
Random vector of 10 32-bit elements: QVector(2860083689, 2145745868, 2656535484, 3483262195, 1958999858, 1693606824, 1357929755, 2049135827, 3882034458, 2129956364)
operator() returned 85841449
Min value: 0
Max value: 4294967295
gen1 and gen2 are different
gen1 and gen2 are the same
operator() returned 853323747
A Graphical Demo
There is a lot of mathematics behind algorithms for generating sequences of pseudorandom numbers and testing them for randomness. A very simple test of the randomness of a generator is to visualize it graphically in some way. I wrote a simple demo(6) that fills a main window with random colored pixels every second.
Here is a typical screen. In my case it appeared quite random:
An alternative version of the code plots random pixel positions. It also looks quite random:
I tried it using QRandomGenerator as well as the Qt qrand() function and standard library rand() function. The results were similar.
The program also displays the time required to generate a window of random data. This will typically vary depending on the random generator source. On my system it took about 110 milliseconds to generate the default 500x500 window. I encourage you to try this and the previous example on your system and see what results you get with different random generator sources.
Summary
If you have a need for random numbers beyond just integer values, the new QRandomGenerator class is now available to help you do this in a platform-independent way.
References
- http://scruss.com/blog/2013/06/07/well-that-was-unexpected-the-raspberr…
- https://github.com/vesteraas/RaspberryPiBitcoinWalletGenerator/wiki/Ste…
- https://www.kernel.org/doc/Documentation/hw_random.txt
- http://doc.qt.io/qt-5/qrandomgenerator.html
- ftp://ftp.ics.com/pub/pickup/randomdemo1.zip
- ftp://ftp.ics.com/pub/pickup/randomdemo2.zip