CS253: Software Development with C++

Spring 2019

Random Numbers

Show Lecture.RandomNumbers 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

Ranges

Not all generators have the same range:

mt19937_64 mt;
minstd_rand mr;

cout << "mt19937_64: " << mt.min() << "…" << mt.max() << '\n'
     << "minstd_rand " << mr.min() << "…" << mr.max() << '\n';
mt19937_64: 0…18446744073709551615
minstd_rand 1…2147483646

Save/Restore

You can save & restore the state of a generator to an I/O stream:

ranlux24 gen;
ofstream("state") << gen;
system("ls -l state");
cout << gen() << '\n';
cout << gen() << '\n';
cout << gen() << '\n';
cout << gen() << "\n\n";
ifstream("state") >> gen;
cout << gen() << '\n';
cout << gen() << '\n';
cout << gen() << '\n';
cout << gen() << '\n';
-rw------- 1 cs253 class 208 Jul 17 08:42 state
15039276
16323925
14283486
7150092

15039276
16323925
14283486
7150092

True randomness

random_device a, b, c;
cout << a() << '\n'
     << b() << '\n'
     << c() << '\n';
132829499
3469946000
1029623485

Cloudflare

The hosting service Cloudflare uses a unique source of randomness.

Seeding

minstd_rand a, b, c(123);
cout << a() << ' ' << a() << '\n';
cout << b() << ' ' << b() << '\n';
cout << c() << ' ' << c() << '\n';
48271 182605794
48271 182605794
5937333 985676192

Seed with random_device

random_device gen;
auto seed = gen();
minstd_rand0 a(seed);
for (int i=0; i<5; i++)
    cout << a() << '\n';
432423612
658985436
995055273
1438814122
1483083234

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';
1371944655
1039735319
223269412
1364846006
1978232960

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';
980786537
122445765
704525771
612458049
1702598677

Better Seeding

Not good enough.

Distributions

  • Uniform:
    • uniform_int_distribution
    • uniform_real_distribution
  • 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 2 6 6 5 2 4 6 2 6 2 2 2 3 5 5 5 5 2 3 5 2 3 5 4 3 3 2 4 6 2 3 3 1 1 2 3 3 5 
4 4 1 6 1 3 4 3 2 4 5 3 3 2 3 2 3 2 4 2 2 1 1 5 4 4 5 6 1 1 5 2 3 5 1 3 5 3 5 5 
4 4 2 3 4 3 4 3 1 6 3 6 6 2 1 3 6 3 4 1 6 4 2 4 6 6 3 2 3 3 2 4 3 6 5 6 6 3 5 6 
2 3 5 3 3 4 1 4 2 3 2 4 4 2 2 4 6 2 1 6 2 1 5 1 1 3 3 4 2 6 3 6 6 6 3 1 1 6 4 2 
6 5 6 2 1 2 3 5 2 3 2 3 4 3 2 3 5 6 6 2 5 1 3 6 3 4 1 3 5 3 1 6 3 2 5 2 3 3 2 6 
2 6 3 3 1 3 4 6 2 1 1 5 3 4 5 2 4 4 4 5 5 4 2 2 2 2 1 3 5 2 6 3 6 6 4 3 1 4 4 2 
2 5 6 3 4 4 6 1 1 1 3 5 6 2 1 5 5 2 6 5 6 1 4 5 6 2 2 5 1 6 4 3 1 1 5 1 1 5 2 3 
2 2 2 4 5 3 4 1 4 2 5 4 1 2 6 4 1 5 3 2 6 6 2 3 6 2 2 5 6 3 5 4 3 2 4 5 4 1 2 2 
6 3 4 5 6 1 1 6 3 2 2 6 3 3 6 3 2 1 4 2 2 2 4 6 6 4 3 2 1 3 1 3 3 1 2 1 1 1 4 6 
2 6 1 4 1 3 4 6 1 2 2 4 2 3 4 4 3 1 2 1 3 6 1 5 6 4 2 4 6 6 3 4 6 5 1 4 4 2 1 2 

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';
}
19.627 19.958 21.222 23.682 19.411 18.099 24.324 24.259 22.658 20.817 
22.057 22.609 24.819 20.316 20.263 23.320 24.985 22.111 24.381 19.705 
18.683 19.126 23.991 20.393 20.474 24.770 23.474 18.388 24.993 24.860 
21.511 24.079 21.540 20.947 22.545 24.611 24.343 23.346 23.374 21.587 
20.470 23.661 24.715 24.746 24.552 23.430 23.472 20.162 19.019 24.797 
23.798 21.780 22.472 21.135 23.830 20.888 21.550 18.620 22.167 22.132 
18.330 21.858 22.553 19.952 21.749 20.696 20.538 23.027 24.382 23.194 
21.511 24.504 21.501 21.561 24.418 22.930 24.494 24.842 18.215 22.139 
23.588 21.116 23.492 21.939 24.413 20.230 19.100 20.099 23.839 19.750 
20.336 19.007 19.511 19.051 20.002 19.764 18.206 18.649 23.744 23.603 

Binding

auto seed = random_device()();
minstd_rand gen(seed);
uniform_real_distribution<float> dist(18.0, 25.0);
auto r = bind(dist, gen);
for (int y=0; y<10; y++) {
    for (int x=0; x<10; x++)
        cout << fixed << setprecision(3) << r() << ' ';
    cout << '\n';
}
23.011 23.048 19.500 21.385 23.820 22.104 18.185 24.679 22.164 20.331 
24.028 18.613 23.550 22.909 21.422 21.309 21.615 23.691 21.662 23.801 
21.736 21.187 20.219 23.336 18.150 19.530 22.584 21.095 18.176 20.135 
21.214 24.577 23.362 21.452 22.582 18.353 19.166 21.067 19.477 20.690 
19.465 22.136 23.560 22.297 18.947 20.552 20.314 22.438 22.799 20.931 
24.124 18.861 24.549 24.352 24.196 21.313 21.732 21.732 21.015 21.490 
18.280 22.426 24.363 20.712 19.468 20.035 20.367 23.983 23.946 20.276 
24.388 18.987 20.877 23.599 23.971 24.667 24.676 22.062 22.371 23.545 
18.467 24.168 21.253 24.321 23.631 24.770 20.750 23.996 23.479 22.615 
21.185 20.873 18.751 22.840 19.764 19.401 20.171 20.026 22.821 23.123 

Binding with temporaries

auto seed = random_device()();
auto r = 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) << r() << ' ';
    cout << '\n';
}
19.132 24.844 22.683 23.843 21.159 22.697 19.280 23.672 20.761 18.501 
23.012 20.368 23.374 22.339 19.992 22.069 22.675 20.592 23.095 19.271 
18.992 22.638 19.729 18.061 19.321 24.574 20.796 19.024 21.295 18.076 
22.977 24.975 21.819 18.170 19.741 20.517 20.399 22.283 24.112 20.890 
20.837 23.476 24.550 18.898 19.300 22.379 24.838 23.359 18.860 19.384 
23.590 22.588 21.699 21.989 23.084 21.981 20.908 20.635 22.891 22.584 
19.413 24.307 24.882 21.498 24.177 18.770 20.591 23.509 19.464 20.218 
22.408 19.543 19.627 21.587 23.125 18.008 22.935 19.949 24.775 23.673 
23.078 23.010 18.454 18.374 21.432 24.417 21.499 20.337 18.210 18.261 
21.554 24.465 19.137 21.014 23.076 22.654 18.095 19.865 23.163 20.128 

Boolean Values

Yield true 42% of time:

auto seed = random_device()();
constexpr int nrolls=100000;

auto r = bind(bernoulli_distribution(0.42), knuth_b(seed));

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

Histogram

auto seed = random_device()();
mt19937_64 gen(seed);
normal_distribution<double> dist(21.5, 1.5);
auto r = bind(dist, gen);
map<int,int> tally;
for (int i=0; i<10000; i++)
    tally[r()]++;
for (auto p : tally)
    cout << p.first << ": " << string(p.second/100,'#') << '\n';
16: 
17: 
18: ###
19: ##########
20: #####################
21: ##########################
22: #####################
23: ##########
24: ###
25: 
26: 
27: 

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: vbqrmhgywgsm
Password: eeuacvaklonb
Password: jzlpodefiuhs
Password: ukpqxhqupmcu
Password: mlupyekkygqs
Password: mkrmqqttqneg
Password: yicqxsdpvxpi
Password: xjexethpgevh

Even though we’re using uniform_int_distribution, it’s uniform_int_distribution<char>, so we get characters. Think of them as 8-bit integers that display differently.

Passwords

With binding:

auto seed = random_device()();
ranlux24 gen(seed);
uniform_int_distribution<char> dist('a','z');
auto r = bind(dist, gen);
for (int y=0; y<8; y++) {
    string pw;
    for (int x=0; x<12; x++)
        pw += r();
    cout << "Password: " << pw << '\n';
}
Password: gazpfwxjthml
Password: tmvyxtjjzfgr
Password: wvaigkejlfqg
Password: vsrmaykfcqbg
Password: lhoqcovzrmxz
Password: gxxgtksbubuy
Password: gygsgmcfgpvd
Password: vooptzgzgsao

Passwords

With extreme binding:

auto r = 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 += r();
    cout << "Password: " << pw << '\n';
}
Password: jmitmqwytgyv
Password: oltejenedozw
Password: unsbgcqidzne
Password: slvmcmzpamfd
Password: iffzfudrrknz
Password: mydsuzuxxiys
Password: imgdotuxpyhh
Password: yeasswqaruay

User: Guest

Check: HTML CSS
Edit History Source

Modified: 2019-05-15T09:37

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