“Computers can’t do anything truly random. Only a person can do that.”
There are several C random number generators, of varying degrees of standardization:
They still work ok, but avoid them for new C++ code. They mix up generation and distribution something terrible.
Traditional random number generators work like this:
long long n = 1; for (int i=0; i<5; i++) { n = n * 16807 % 2147483647; cout << n << '\n'; }
16807 282475249 1622650073 984943658 1144108930
Engine | Description |
---|---|
default_random_engine | Default random engine |
minstd_rand | Minimal Standard minstd_rand generator |
minstd_rand0 | Minimal Standard minstd_rand0 generator |
mt19937 | Mersenne Twister 19937 generator |
mt19937_64 | Mersenne Twister 19937 generator (64 bit) |
ranlux24_base | Ranlux 24 base generator |
ranlux48_base | Ranlux 48 base generator |
ranlux24 | Ranlux 24 generator |
ranlux48 | Ranlux 48 generator |
knuth_b | Knuth-B generator |
random_device | True random number generator |
Define a random-number generator, and use ()
to generate a number.
This is not a function call, because gen
is an object, not a
function. It’s operator()
.
That sequence looks familiar …
#include <random> #include <iostream> using namespace std; int main() { default_random_engine gen; for (int i=0; i<5; i++) cout << gen() << '\n'; }
16807 282475249 1622650073 984943658 1144108930
I won’t bother with the #include
s in subsequent examples.
Here’s a different, 64-bit generator. You can use .min()
and .max()
to find out the range of a given generator.
mt19937_64 gen; cout << "min=" << gen.min() << '\n' << "max=" << gen.max() << "\n\n"; for (int i=0; i<5; i++) cout << gen() << '\n';
min=0 max=18446744073709551615 14514284786278117030 4620546740167642908 13109570281517897720 17462938647148434322 355488278567739596
random_device gen; for (int i=0; i<3; i++) cout << gen() << '\n';
376627969 2368025353 2921612877
random_device
is, ideally, truly random, and not pseudo-random.
The hosting service Cloudflare uses a unique source of randomness.
minstd_rand a; cout << a() << ' ' << a() << '\n'; minstd_rand b; cout << b() << ' ' << b() << '\n'; minstd_rand c(123); cout << c() << ' ' << c() << '\n';
48271 182605794 48271 182605794 5937333 985676192
minstd_rand a, b(1), c(123), d(8675309), e(8675309); cout << a() << '\n' << b() << '\n' << c() << '\n' << d() << '\n' << e() << '\n';
48271 48271 5937333 6529574 6529574
random_device gen; auto seed = gen(); minstd_rand0 a(seed); for (int i=0; i<5; i++) cout << a() << '\n';
884593335 330893164 1486245265 1941870598 1710157127
You can seed with random_device
, if you know that
it’s truly random.
// seconds since start of 1970 auto seed = time(nullptr); minstd_rand a(seed); for (int i=0; i<5; i++) cout << a() << '\n';
711026239 863936415 1089747372 583460547 2123517479
C++ provides a more accurate time—nanoseconds make more possibilities:
auto seed = chrono::high_resolution_clock::now() .time_since_epoch().count(); minstd_rand a(seed); for (int i=0; i<5; i++) cout << a() << '\n';
679812404 1674427324 1439334665 585182824 1507688313
|
|
auto seed = random_device()(); // ❓❓❓ mt19937 gen(seed); uniform_int_distribution<int> dist(1,6); for (int y=0; y<10; y++) { for (int x=0; x<40; x++) cout << dist(gen) << ' '; cout << '\n'; }
2 6 5 2 2 4 5 3 3 3 1 4 1 4 2 5 4 3 2 1 2 4 2 3 6 6 6 1 1 6 5 1 2 4 1 1 5 6 6 1 2 3 4 5 5 3 5 4 2 3 6 3 6 2 5 5 5 1 2 6 1 2 2 6 2 5 5 4 4 4 3 4 5 2 2 2 4 2 4 3 2 5 5 3 3 6 4 1 2 4 1 2 6 4 4 3 3 6 1 5 3 2 4 6 5 6 2 5 4 3 1 6 2 1 2 1 4 4 4 3 2 2 1 3 4 6 1 3 4 1 6 6 1 1 4 3 2 5 2 6 5 5 4 4 4 5 6 3 4 3 6 1 4 4 4 2 1 4 4 3 3 5 6 2 4 5 5 1 6 4 4 4 2 5 4 3 5 1 6 5 2 1 5 6 3 6 5 5 1 4 3 6 1 1 2 4 2 2 3 4 3 4 2 2 3 2 5 3 6 4 5 4 5 6 5 4 1 4 1 1 1 6 5 3 3 5 2 6 4 2 2 4 4 2 6 4 6 2 1 2 3 3 3 3 1 1 6 6 4 6 6 3 5 1 4 6 4 5 2 4 6 6 5 3 4 1 2 6 3 3 1 6 3 1 6 2 6 3 6 5 3 5 6 4 2 2 5 4 1 4 2 2 4 4 5 2 5 5 3 2 4 3 5 3 4 3 5 5 4 2 2 4 2 5 6 6 2 6 6 2 2 1 6 4 6 6 1 1 1 1 2 6 3 5 1 3 4 1 4 1 2 6 5 2 5 1 3 2 5 1 3 4 1 3 1 5 1 2 1 5 4 3 5 3 3 1 2 6 3 2 6 6 5 2 2 6 3 3 1 5 1 5 6 2 1 6 2 1 2 3 1 5 1 2 4 1 4 2 4 5
auto seed = random_device()(); ranlux48 gen(seed); uniform_real_distribution<float> dist(18.0, 25.0); for (int y=0; y<10; y++) { for (int x=0; x<10; x++) cout << fixed << setprecision(3) << dist(gen) << ' '; cout << '\n'; }
20.742 19.659 22.310 19.358 19.334 19.400 19.891 23.177 23.786 20.739 21.115 20.122 19.239 21.037 19.101 20.132 18.026 22.282 20.439 23.295 24.777 19.551 18.753 20.336 20.609 23.638 20.090 20.675 23.588 21.010 21.576 23.063 18.876 23.569 19.184 18.253 20.651 19.567 19.821 21.931 18.314 19.293 18.333 24.326 19.919 21.640 24.098 19.901 22.323 20.097 18.949 22.715 23.755 23.453 20.423 18.498 19.640 22.209 19.728 23.220 19.234 19.364 22.181 22.992 21.018 20.977 20.131 24.749 19.911 23.538 22.696 22.641 18.629 19.806 18.004 21.973 23.282 21.156 23.739 19.073 21.207 20.269 23.459 21.013 20.686 21.492 18.788 20.795 19.998 23.368 24.807 22.508 24.768 23.344 22.012 18.400 22.999 22.947 20.133 24.982
auto seed = random_device()(); minstd_rand gen(seed); uniform_real_distribution<float> dist(18.0, 25.0); auto rng = bind(dist, gen); for (int y=0; y<10; y++) { for (int x=0; x<10; x++) cout << fixed << setprecision(3) << rng() << ' '; cout << '\n'; }
20.630 22.694 18.326 22.125 18.321 19.833 24.710 18.464 24.888 18.294 20.277 21.909 19.700 19.239 19.819 22.992 20.536 23.333 23.180 24.378 19.051 20.986 19.359 23.414 23.641 23.058 20.097 19.569 24.275 20.152 22.634 22.494 22.045 22.104 18.275 22.683 22.157 20.325 18.830 22.700 18.801 23.355 22.912 20.659 24.556 23.103 20.268 24.297 23.798 18.608 18.438 22.405 22.949 21.597 18.766 24.958 22.792 19.491 22.756 19.761 19.630 18.848 22.680 22.187 18.649 24.347 18.177 20.432 21.613 20.893 21.226 20.306 24.828 21.593 19.427 21.673 24.145 22.097 20.980 23.584 23.819 19.607 24.423 24.206 18.204 22.662 21.968 24.444 20.998 19.651 22.621 22.701 21.010 19.428 19.831 18.268 22.032 18.932 23.384 24.597
auto seed = random_device()(); auto rng = bind(uniform_real_distribution<float>(18.0, 25.0), mt19937(seed)); for (int y=0; y<10; y++) { for (int x=0; x<10; x++) cout << fixed << setprecision(3) << rng() << ' '; cout << '\n'; }
24.397 23.492 20.856 18.809 19.249 22.621 21.830 23.532 20.667 21.941 24.725 18.851 19.309 18.938 18.748 21.109 24.845 19.965 20.772 24.544 22.409 20.064 18.263 19.101 22.839 22.892 21.031 23.541 22.858 23.878 21.073 23.166 21.722 18.215 23.625 21.440 24.919 20.569 20.166 24.029 20.755 22.053 19.587 19.242 19.435 24.432 19.384 20.962 22.450 19.146 18.342 22.615 21.768 22.163 20.893 22.812 22.259 22.923 21.968 20.932 19.549 19.507 21.821 18.059 22.063 21.502 24.525 18.218 19.774 24.815 19.887 20.032 18.097 20.189 21.718 24.199 21.952 23.933 18.529 24.538 22.108 20.848 22.722 23.992 24.448 20.319 22.335 22.846 24.005 18.316 19.885 22.456 23.985 23.764 22.097 20.219 19.859 19.789 21.230 18.599
Create a RNG that yields true
42% of time:
auto seed = random_device()(); constexpr int nrolls=10000; auto rng = bind(bernoulli_distribution(0.42), knuth_b(seed)); int count=0; for (int i=0; i<nrolls; i++) if (rng()) count++; cout << "true: " << count*100.0/nrolls << "%\n";
true: 41.61%
auto seed = random_device()(); mt19937_64 gen(seed); normal_distribution<double> dist(21.5, 1.5); auto rng = bind(dist, gen); map<int,int> tally; for (int i=0; i<10000; i++) tally[rng()]++; for (auto p : tally) cout << p.first << ": " << string(p.second/100,'#') << '\n';
14: 15: 16: 17: 18: ### 19: ########### 20: ##################### 21: ########################## 22: #################### 23: ########### 24: ### 25: 26: 28:
random_device rd; auto seed = rd(); ranlux24 gen(seed); uniform_int_distribution<char> dist('a','z'); for (int y=0; y<8; y++) { string pw; for (int x=0; x<12; x++) pw += dist(gen); cout << "Password: " << pw << '\n'; }
Password: ywmqppgoicli Password: fznonjcfziqi Password: kxgqdsvziqya Password: sjzqoygdrqee Password: lskakaoxreex Password: dzxqfbjoomyn Password: jchfzcrlouwk Password: flutywuikddr
Even though we’re using uniform_int_distribution
,
we get characters. They’re like small integers.
With binding:
auto seed = random_device()(); ranlux24 gen(seed); uniform_int_distribution<char> dist('a','z'); auto rng = bind(dist, gen); for (int y=0; y<8; y++) { string pw; for (int x=0; x<12; x++) pw += rng(); cout << "Password: " << pw << '\n'; }
Password: btfwfqdfuzya Password: seqhpvzuqslr Password: hmqvzilpaldi Password: ffpyvsfjochp Password: sdixsiopunyy Password: ewrtsfaalxds Password: qrbvuliqogel Password: qcufemvsmvpl
With extreme binding:
auto rng = bind(uniform_int_distribution<char>('a','z'), ranlux24((random_device())())); for (int y=0; y<8; y++) { string pw; for (int x=0; x<12; x++) pw += rng(); cout << "Password: " << pw << '\n'; }
Password: xnulcnnyhyze Password: vfqnhmmswxew Password: inalnojfkaka Password: orlotepxxojq Password: zpbwrlanaqcf Password: bginppsjfiaa Password: xmmyneyovsea Password: fgbslzvrphwz