Examples based on this talk.
Below is the old and rusty way of generating random numbers. Don’t do it!
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { srand((unsigned int)time(NULL)); for(int n = 0; n < 10; ++n) printf("%d ", rand() % 1000); printf("\n"); } |
Below is the new and shiny way of generating random numbers. Do that instead! Comments inline and a benchmark of each random number generator included. Program output first:
random_device min = 0, max = 4294967295
mt19937 min = 0, max = 4294967295
mt19937_64 min = 0, max = 18446744073709551615
10 -1 6 10 5 -4 -3 2 6 -3
8.73366 3.81724 2.11837 4.14365 9.58442
vector of ints: 0 1 2 3 4 5 6 7 8 9
shuffled to : 3 1 6 7 9 4 8 5 0 2generating 100000000 random numbers…
Program output.
random_device duration in ms = 142080
mt19937 duration in ms = 553.894
uniform_int_distribution duration in ms = 2719.63
uniform_real_distribution duration in ms = 1070.29
Complete listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#include <iostream> #include <random> #include <algorithm> #include <vector> #include <chrono> using namespace std; using namespace chrono; const int COUNT = 100'000'000; int main() { /* random_device MAY BE CRYPTOGRAPHICALLY STRONG */ random_device rd; /* SEED mt19937 and mt19937_64 ENGINE WITH STRONG RANDOM NUMBER */ mt19937 mt(rd()); mt19937_64 mt64(rd()); /* CREATE RANDOM DISTRIBUTION OBJECTS FOR INTS AND DOUBLES */ uniform_int_distribution int_dist(-10, 10); uniform_real_distribution real_dist(1.0, 10.0); /* PRINT MIN AND MAX OF EACH RANDOM ENGINE */ cout << "random_device min = " << rd.min() << ", max = " << rd.max() << endl; cout << "mt19937 min = " << mt.min() << ", max = " << mt.max() << endl; cout << "mt19937_64 min = " << mt64.min() << ", max = " << mt64.max() << endl; /* GENERATE 10 INTEGERS IN RANGE -10 TO 10 USING mt19937 GENERATOR */ for(int n = 0; n < 10; ++n) cout << int_dist(mt) << " "; cout << endl; /* GENERATE 5 DOUBLES IN RANGE 1.0 TO 10.0 USING mt19937 GENERATOR */ for(int n = 0; n < 5; ++n) cout << real_dist(mt) << " "; cout << endl; /* GENERATE A VECTOR OF CONSECUTIVE INTEGERS */ vector<int> v; for(int n = 0; n < 10; ++n) v.push_back(n); /* PRINT IT */ cout << "vector of ints: "; for(auto it : v) cout << it << " "; cout << endl; /* RANDOMLY SHUFFLE THE VECTOR OF INTEGERS USING mt19937 GENERATOR */ shuffle(begin(v), end(v), mt); /* PRINT IT */ cout << "shuffled to : "; for(auto it : v) cout << it << " "; cout << endl; /* ********************* */ /* BENCHMARK STARTS HERE */ /* ********************* */ cout << "generating " << COUNT << " random numbers..." << endl; /* TEST PERFORMANCE OF random_device */ auto start = high_resolution_clock::now(); int result{0}; for(int i = 0; i < COUNT; ++i) result += rd(); auto end = high_resolution_clock::now(); auto duration = duration_cast<microseconds>(end - start).count() / 1000.0f; cout << "random_device duration in ms = " << duration << endl; /* TEST PERFORMANCE OF mt19937 */ start = high_resolution_clock::now(); result = 0; for(int i = 0; i < COUNT; ++i) result += mt(); end = high_resolution_clock::now(); duration = duration_cast<microseconds>(end - start).count() / 1000.0f; cout << "mt19937 duration in ms = " << duration << endl; /* TEST PERFORMANCE OF uniform_int_distribution */ start = high_resolution_clock::now(); result = 0; for(int i = 0; i < COUNT; ++i) result += int_dist(mt); end = high_resolution_clock::now(); duration = duration_cast<microseconds>(end - start).count() / 1000.0f; cout << "uniform_int_distribution duration in ms = " << duration << endl; /* TEST PERFORMANCE OF uniform_real_distribution */ start = high_resolution_clock::now(); result = 0; for(int i = 0; i < COUNT; ++i) result += real_dist(mt); end = high_resolution_clock::now(); duration = duration_cast<microseconds>(end - start).count() / 1000.0f; cout << "uniform_real_distribution duration in ms = " << duration << endl; } |
.
Do you have a general advice when to use mt19937 and when to use mt19937_64? How is the performance compared to srand()?
_64 gives you wider range of random numbers. I haven’t tested rand() but you could easily modify my program to do so and post the results here 😉
Yes, was just trying to out your example and realized that std::random_device is not reliable under windows with gcc 🙁 “Unlike VC, GCC hasn’t implemented random_device nondeterministically on Windows”
https://stackoverflow.com/questions/18880654/why-do-i-get-the-same-sequence-for-every-run-with-stdrandom-device-with-mingw