# Philosophy

“Computers can’t do anything truly random. Only a person can do that.”

• Stop trying to prove your superiority.
• My dog is pretty random.
• You’re somewhat predictable.
• An online rock-paper-scissors program beats people 60% of the time over more than a million games, because people are lousy at being random.

Traditionally, random number generators work like this:

```unsigned n = 1;
for (int i=0; i<5; i++) {
n = n * 16807 % 2147483647;
cout << n << '\n';
}
```
```16807
282475249
1622647863
947787490
1578127215
```
• Good enough for many tasks. However …
• How many possible states does this RNG have?
• What number always follows 16807?
• What happens if it goes to zero?

# Overview

• In C++, random numbers have:
• Generators
Generate uniformly-distributed random integers, typically zero or one to a big number.
• Distributions
Take uniformly-distributed random integers, and transform them into other distributions with different ranges.
• Examples:
• Picking a card (uniform, but discrete)
• Rolling 3d6 (bell-shaped, but discrete)
• Human height (bell-shaped, continuous)

# Generators

EngineDescription
`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

# Default Engine

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()`.

```#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.

# Mersenne Twister

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
```

# True randomness

```random_device gen;
for (int i=0; i<3; i++)
cout << gen() << '\n';
```
```2920005270
1577395626
1078648395
```
• `random_device` is, ideally, truly random, and not pseudo-random.
• It might depend on random things like human typing intervals, network packets arrival times, or radioactive decay.
• If true randomness isn’t available, it resorts to pseudo-random numbers.
• It could pause waiting for randomness to become available.
• Use it sparingly.

# Cloudflare

The hosting service Cloudflare uses a unique source of randomness.

# Seeding

```minstd_rand a;
cout << a() << ' ' << a() << '\n';
minstd_rand b;
cout << b() << ' ' << b() << '\n';
minstd_rand c(123);
cout << c() << ' ' << c() << '\n';
```
```182605794 48271
182605794 48271
985676192 5937333
```
• Great—we can “seed” the random number generator with a value.
• This way, we can reproduce our pseudo-random sequences.
• Consider random testing: we want to be able to reproduce the sequence if we find an error.
• Hey, wait—what kind of thing is `c`? It’s clearly a function, because `c()` yielded a number. But it seems to have a constructor …

# Seeding

```minstd_rand a, b(1), c(123), d(8675309);
cout << a() << '\n'
<< b() << '\n'
<< c() << '\n'
<< d() << '\n';
```
```48271
48271
5937333
6529574
```
• Most generators are pseudo-random sequences.
• They can be seeded, though they do have a default seed.
• How to choose the random seed? It should probably be … random.

# Seed with random_device

```random_device gen;
auto seed = gen();
minstd_rand a(seed);
for (int i=0; i<5; i++)
cout << a() << '\n';
```
```2146657799
937680485
261863616
329861694
1310072216
```

You can seed with `random_device`, if you know that it’s truly random.

# Seed with time

```// seconds since start of 1970
auto seed = time(nullptr);
minstd_rand a(seed);
for (int i=0; i<5; i++)
cout << a() << '\n';
```
```666380273
1832093217
1447610700
645709967
488164499
```
• You can seed with the time of day.
• OK for casual use, but the seed is easily guessed.
• There are only 86,400 seconds in a day.

# Seed with more accurate time

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';
```
```1853070982
453023631
77714600
1855008938
1658300886
```

# Better Seeding

• Many generators have more than 32 or 64 bits of state.
• Therefore, you can seed them with more than 32 or 64 bits.
• If you’re doing something very important, and somebody guessing your seed, and hence predicting your sequence, would be catastrophic:
• on-line poker
• extra-marital affairs
• encryption of military communications.
• That’s beyond the scope of this discussion.

# Not good enough.

• Great, so we know how to generate a number 0…2,147,483,647
or perhaps 0…18,446,744,073,709,551,615
• How often do we want that?
• Sometimes, we want integers with different ranges.
• Or, perhaps we want floating-point numbers.
• Maybe spread out linearly, or a bell-shaped curve, Poisson, etc.
• This is a job for a distribution.

# Distributions

 Uniform: `uniform_int_distribution` `uniform_real_distribution` Related to Bernoulli (yes/no) trials: `bernoulli_distribution` `binomial_distribution` `geometric_distribution` `negative_binomial_distribution` Piecewise distributions: `discrete_distribution` `piecewise_constant_distribution` `piecewise_linear_distribution` Related to Normal distribution: `normal_distribution` `lognormal_distribution` `chi_squared_distribution` `cauchy_distribution` `fisher_f_distribution` `student_t_distribution` Rate-based distributions: `poisson_distribution` `exponential_distribution` `gamma_distribution` `weibull_distribution` `extreme_value_distribution`

# uniform_int_distribution

```mt19937 gen;
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';
}
```
```5 1 6 6 1 6 6 2 4 2 1 4 2 2 4 6 6 6 6 6 1 5 6 6 6 1 3 5 5 2 1 1 3 1 6 4 5 6 6 4
4 5 1 3 6 2 6 5 5 3 5 5 5 3 3 3 4 2 2 2 5 5 1 2 2 6 1 1 1 6 5 5 5 1 2 5 6 3 1 4
3 1 3 2 5 1 5 1 2 3 3 3 3 3 4 5 5 6 5 5 2 5 5 1 4 5 1 4 1 3 3 5 6 4 3 6 4 5 2 1
5 5 2 5 4 6 5 3 6 3 6 4 4 1 1 5 1 2 2 5 6 6 2 2 5 2 2 1 6 2 3 6 2 6 2 6 4 5 3 6
3 1 5 5 4 4 4 5 6 5 2 5 5 3 5 4 3 2 4 4 1 3 1 3 4 4 5 3 6 6 1 1 4 3 3 3 1 5 3 3
1 6 5 3 2 6 4 4 1 1 4 3 2 3 4 4 5 5 5 6 3 5 1 6 2 4 6 4 1 4 5 3 4 4 6 3 1 6 3 4
1 3 6 6 1 1 5 6 5 3 6 5 1 3 3 6 2 1 5 4 3 5 6 1 2 6 2 1 1 2 1 5 6 1 4 4 4 1 1 5
6 4 4 3 3 2 4 1 3 3 1 5 2 1 1 2 2 4 2 5 3 6 1 5 6 2 6 1 3 2 3 4 3 6 6 3 3 1 1 1
5 6 3 2 2 2 3 3 1 2 1 3 6 6 6 4 4 5 1 3 2 4 3 1 5 2 1 3 1 6 2 5 4 2 5 1 4 3 3 3
4 5 2 2 5 4 2 5 5 2 2 3 3 3 4 5 5 5 1 3 6 3 5 2 3 6 3 3 3 5 2 1 4 3 4 6 5 3 5 6
```

# uniform_real_distribution

```mt19937 gen;
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';
}
```
```23.703 18.948 24.341 23.845 18.889 24.782 24.394 19.547 22.427 20.157
18.683 21.831 19.949 19.319 21.828 24.950 24.703 24.975 24.754 24.774
19.103 23.081 24.794 24.868 24.700 18.769 21.398 23.587 23.602 20.079
18.993 18.033 20.952 18.787 24.410 22.478 23.545 24.149 24.716 21.526
22.590 23.586 18.250 20.529 23.944 19.483 24.538 22.770 22.751 20.791
23.304 23.185 23.202 21.323 20.746 20.955 22.588 19.217 19.198 20.113
22.942 23.581 18.223 20.216 19.938 24.107 18.323 19.044 18.680 24.958
23.764 23.753 22.864 18.876 20.220 23.346 24.652 21.434 18.241 22.645
21.071 18.881 20.671 19.471 23.359 18.359 23.566 18.255 19.308 20.861
21.428 21.206 21.119 21.413 22.524 23.558 22.966 24.446 23.283 23.653
```

# Binding

```mt19937 gen;
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';
}
```
```23.703 18.948 24.341 23.845 18.889 24.782 24.394 19.547 22.427 20.157
18.683 21.831 19.949 19.319 21.828 24.950 24.703 24.975 24.754 24.774
19.103 23.081 24.794 24.868 24.700 18.769 21.398 23.587 23.602 20.079
18.993 18.033 20.952 18.787 24.410 22.478 23.545 24.149 24.716 21.526
22.590 23.586 18.250 20.529 23.944 19.483 24.538 22.770 22.751 20.791
23.304 23.185 23.202 21.323 20.746 20.955 22.588 19.217 19.198 20.113
22.942 23.581 18.223 20.216 19.938 24.107 18.323 19.044 18.680 24.958
23.764 23.753 22.864 18.876 20.220 23.346 24.652 21.434 18.241 22.645
21.071 18.881 20.671 19.471 23.359 18.359 23.566 18.255 19.308 20.861
21.428 21.206 21.119 21.413 22.524 23.558 22.966 24.446 23.283 23.653
```

# Binding with temporaries

```auto rng = bind(uniform_real_distribution<float>(18.0, 25.0), mt19937());
for (int y=0; y<10; y++) {
for (int x=0; x<10; x++)
cout << fixed << setprecision(3) << rng() << ' ';
cout << '\n';
}
```
```23.703 18.948 24.341 23.845 18.889 24.782 24.394 19.547 22.427 20.157
18.683 21.831 19.949 19.319 21.828 24.950 24.703 24.975 24.754 24.774
19.103 23.081 24.794 24.868 24.700 18.769 21.398 23.587 23.602 20.079
18.993 18.033 20.952 18.787 24.410 22.478 23.545 24.149 24.716 21.526
22.590 23.586 18.250 20.529 23.944 19.483 24.538 22.770 22.751 20.791
23.304 23.185 23.202 21.323 20.746 20.955 22.588 19.217 19.198 20.113
22.942 23.581 18.223 20.216 19.938 24.107 18.323 19.044 18.680 24.958
23.764 23.753 22.864 18.876 20.220 23.346 24.652 21.434 18.241 22.645
21.071 18.881 20.671 19.471 23.359 18.359 23.566 18.255 19.308 20.861
21.428 21.206 21.119 21.413 22.524 23.558 22.966 24.446 23.283 23.653
```

# Boolean Values

```constexpr int nrolls=10000;

auto rng = bind(bernoulli_distribution(0.42), knuth_b());

int count=0;
for (int i=0; i<nrolls; i++)
if (rng())
count++;
cout << "true: " << count*100.0/nrolls << "%\n";
```
```true: 41.93%
```

# Histogram

```mt19937 gen;
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';
```
```15:
16:
17: #
18: ###
19: ###########
20: #####################
21: #########################
22: ####################
23: ###########
24: ###
25:
26:
```

```mt19937 gen;
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: vdxvdzxfqico
```

With binding:

```mt19937 gen;
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: vdxvdzxfqico
```

With extreme binding:

```auto rng = bind(uniform_int_distribution<char>('a','z'), mt19937());
for (int y=0; y<8; y++) {
string pw;
for (int x=0; x<12; x++)
pw += rng();
cout << "Password: " << pw << '\n';
}
```
```Password: vdxvdzxfqico