CS253: Software Development with C++

Fall 2018

Random Numbers

See this page as a slide show

CS253 Random Numbers

Philosophy

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

Old Stuff

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 Method

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

Overview

Generators

EngineDescription
default_random_engineDefault random engine
minstd_randMinimal Standard minstd_rand generator
minstd_rand0Minimal Standard minstd_rand0 generator
mt19937Mersenne Twister 19937 generator
mt19937_64Mersenne Twister 19937 generator (64 bit)
ranlux24_baseRanlux 24 base generator
ranlux48_baseRanlux 48 base generator
ranlux24Ranlux 24 generator
ranlux48Ranlux 48 generator
knuth_bKnuth-B generator
random_deviceTrue 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().










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 #includes 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';
376627969
2368025353
2921612877

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';
48271 182605794
48271 182605794
5937333 985676192

Seeding

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

Seed with random_device

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.

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';
711026239
863936415
1089747372
583460547
2123517479

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';
679812404
1674427324
1439334665
585182824
1507688313

Better Seeding

Not good enough.

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

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 

uniform_real_distribution

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 

Binding

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 

Binding with temporaries

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 

Boolean Values

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%

Histogram

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: 

Passwords

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.

Passwords

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

Passwords

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

User: Guest

Check: HTML CSS
Edit History Source

Modified: 2018-12-03T21:28

Apply to CSU | Contact CSU | Disclaimer | Equal Opportunity
Colorado State University, Fort Collins, CO 80523 USA
© 2018 Colorado State University
CS Building