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 Apr 29 01:16 state
15039276
16323925
14283486
7150092

15039276
16323925
14283486
7150092

True randomness

random_device a, b, c;
cout << a() << '\n'
     << b() << '\n'
     << c() << '\n';
1547924466
2013979236
4029786719

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';
1744175433
1204720881
1268023051
35705329
951526990

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';
1312563790
1556669649
1547818349
1635961802
95993211

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';
1081591864
2045924927
364192981
658251509
290549927

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 3 2 5 3 4 5 4 1 1 3 1 1 3 2 6 5 6 3 4 6 4 1 1 4 1 2 6 5 3 5 1 6 3 6 1 6 6 1 2 
2 5 1 1 1 3 1 1 4 4 4 4 1 1 1 6 6 3 3 1 2 2 3 6 1 6 1 6 3 1 6 4 2 5 6 1 6 5 4 4 
4 1 5 1 5 2 6 3 3 1 2 2 5 6 3 4 1 5 6 6 1 6 3 5 5 2 3 6 2 1 5 6 3 2 3 6 1 1 3 6 
2 3 4 4 5 5 5 6 2 1 5 6 2 4 2 2 6 4 1 4 6 4 3 1 3 1 5 6 3 3 3 1 4 6 2 1 6 3 4 4 
5 6 6 2 3 2 1 1 4 2 4 2 4 4 4 5 1 2 2 5 5 1 1 4 4 4 2 4 5 5 5 6 3 1 4 5 1 1 3 3 
3 3 5 6 5 5 2 5 2 4 4 1 5 1 1 6 6 3 4 1 2 4 6 5 1 3 2 5 5 1 2 5 2 5 5 6 2 2 3 2 
5 3 6 4 6 6 6 6 2 2 2 1 5 6 5 3 2 2 3 1 5 3 3 1 6 1 5 3 4 6 2 3 6 1 1 5 5 5 6 4 
4 5 5 3 3 1 2 4 2 2 6 6 4 5 6 4 3 6 6 4 3 6 1 2 5 6 4 4 5 2 2 2 4 6 4 6 4 3 2 6 
2 4 1 4 1 6 1 4 6 6 3 2 6 6 2 3 3 5 1 5 1 4 2 1 2 5 3 2 1 5 5 6 2 6 1 2 5 2 2 1 
2 1 2 3 2 6 5 4 3 5 4 2 3 3 3 3 2 4 4 4 3 3 5 1 1 6 5 5 6 5 3 4 5 4 4 5 6 4 4 1 

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';
}
23.807 24.696 20.495 21.270 23.027 22.570 20.478 20.472 19.556 19.845 
18.252 18.716 18.112 18.013 19.430 18.144 23.610 22.549 19.702 18.244 
21.874 18.896 20.716 24.366 18.808 18.633 22.016 21.507 23.856 18.188 
24.906 20.105 24.098 20.726 24.114 21.968 24.206 20.154 22.552 22.208 
19.598 24.176 21.261 19.947 23.809 19.891 24.743 24.027 18.880 23.210 
24.690 18.917 24.302 18.304 20.689 19.046 20.782 24.050 24.373 22.115 
19.421 23.370 20.882 18.219 24.364 18.358 21.328 23.769 19.007 24.005 
24.712 21.792 20.509 18.915 19.672 22.179 21.317 19.200 22.139 24.171 
21.780 24.463 24.484 23.364 24.134 19.537 20.097 19.486 24.130 18.834 
19.151 24.793 24.377 23.512 20.493 24.341 21.757 24.258 20.811 20.668 

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';
}
18.903 22.405 18.975 24.661 21.501 19.735 23.305 22.372 24.782 18.994 
18.977 19.551 23.821 23.356 24.533 18.791 19.919 22.863 20.671 19.336 
18.874 23.171 24.506 20.269 24.970 24.367 21.120 20.787 19.842 21.268 
23.465 19.147 19.624 24.079 21.563 22.702 18.993 21.993 24.274 20.711 
18.275 24.697 18.064 22.518 23.201 21.531 19.005 21.189 19.463 22.051 
24.271 24.583 20.205 21.077 19.971 22.531 24.883 21.803 23.524 18.169 
23.144 24.326 18.946 23.484 18.442 24.233 23.997 21.127 24.346 19.460 
22.392 23.774 21.331 22.155 19.017 24.399 22.920 20.375 22.671 20.289 
22.667 24.495 23.599 23.716 21.478 20.014 19.862 21.514 22.985 18.480 
23.065 21.124 23.317 22.492 21.076 20.978 21.798 24.073 23.831 24.904 

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';
}
20.305 21.909 24.621 21.190 22.446 23.809 20.605 19.520 20.306 24.075 
23.551 18.796 22.912 22.871 18.007 20.111 19.674 23.151 24.007 19.192 
20.610 23.895 19.319 24.525 19.796 22.093 24.383 20.191 22.899 19.750 
23.129 24.989 23.700 23.888 23.437 23.665 23.814 24.495 22.687 24.796 
22.994 18.522 23.621 23.952 21.300 24.267 22.909 18.934 19.577 19.822 
21.068 18.854 20.461 21.406 19.448 19.301 20.265 21.277 18.584 18.951 
19.163 23.475 18.742 23.704 20.167 24.118 21.453 20.028 19.908 20.993 
24.232 22.151 19.097 19.722 19.041 23.228 22.121 19.667 19.326 20.634 
20.433 21.320 18.650 23.936 22.890 19.211 19.012 21.669 20.284 18.030 
24.861 24.968 20.414 21.672 24.423 23.390 22.315 22.393 21.677 22.077 

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.104%

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

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: baqmwxjevxvk
Password: wolepnphwaih
Password: hbfwhlgoyuyy
Password: swzqfrzeroan
Password: xytopgcpmdub
Password: zhausthkiish
Password: wgmculzisnmi
Password: skgwfiygmozo

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: qdnsmhgwsfjm
Password: pvulxmwzdtti
Password: oagkynxujfie
Password: qcdrvovluwsq
Password: wmoyqvtpouxl
Password: nfvsjzutwbha
Password: nwoggxnrgvep
Password: uttmfqanldrf

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: xwhtamcivcdr
Password: vpnpsfoxluqe
Password: prmsgjyvjatc
Password: gtyyjauqosej
Password: pggggmgszcoe
Password: prlhexfyqgtg
Password: cjrvezbdymaw
Password: mahgnxxyuojr

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