Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I keep getting the same first digit while I've seeded a generator with time?

Tags:

c++

I don't understand why I keep getting the same 1st digit when I've already seeded a default_random_engine with time(0)(C++ Primer tell me to usetime(0)). Is it a problem of my computer? (Ubuntu, C++11)

I tried on a online compiler and it's interesting that I got the same 1st digit using gcc while not using clang++.

https://wandbox.org/permlink/kiUg1BW1RkDL8y8c

Code:

#include <iostream>
#include <ctime>
#include <random>
using namespace std;
int main(){
    auto t = time(0);
    cout << "time: " << t << endl;
    default_random_engine e(t);
    uniform_int_distribution<int> uniform_dist(0, 9);
    cout << "sequence:";
    for(int i = 0; i < 10; i++){
        cout << uniform_dist(e);
    }
    cout << endl;
    return 0;
}

Result:

As you can see I keep getting 6 as the first digit of a random number, no matter I use clang++ or g++ to compile. enter image description here

like image 720
Rick Avatar asked Nov 02 '18 12:11

Rick


2 Answers

You are setting initial states into your random-generator that are very similar. Depending on the quality of the generator, this may or may not result in similar outputs. To illustrate, I've augmented your sample to (a) print only the first sequence, since that is what we care about, and (b) print several results of various resolution:

int main(){
    auto t = time(0);
    cout << "time: " << t << endl;
    default_random_engine e(t);
    default_random_engine e2(t);
    default_random_engine e3(t);
    default_random_engine e4(t);
    uniform_int_distribution<int> uniform_dist(0, 9);
    uniform_int_distribution<int> uniform_dist2(0,999);
    uniform_int_distribution<int> uniform_dist3(0,99999);
    uniform_int_distribution<int> uniform_dist4(0,9999999);
    cout << "sequence: ";
    cout << uniform_dist(e) << " " << uniform_dist2(e2) << " " << uniform_dist3(e3) << " " << uniform_dist4(e4);
    cout << endl;
    return 0;
}

When run:

$ ./a.out
time: 1541162210
sequence: 7 704 70457 7070079
$ ./a.out
time: 1541162211
sequence: 7 704 70457 7070157
$ ./a.out
time: 1541162212
sequence: 7 704 70458 7070236
$ ./a.out
time: 1541162213
sequence: 7 704 70459 7070315
$ ./a.out
time: 1541162214
sequence: 7 704 70460 7070393
$ ./a.out
time: 1541162215
sequence: 7 704 70461 7070472
$ ./a.out
time: 1541162216
sequence: 7 704 70461 7070550
$ ./a.out
time: 1541162217
sequence: 7 704 70462 7070629
$ ./a.out
time: 1541162218
sequence: 7 704 70463 7070707
$ ./a.out
time: 1541162219
sequence: 7 704 70464 7070786

While I do not know exactly what this random-generator implementation is doing, you can easily see that it is performing a very simple transformation of your seed into state, and state into output values. As other comments have suggested, there are better random generators and better seeds. Also note that the quality varies between implementations; Visual Studio 2017 does not exhibit this behavior.

like image 65
Wheezil Avatar answered Nov 19 '22 14:11

Wheezil


As suggested in comments, std::random_device will provide a superior source of seed material compared to time().

However, if there is a need to use a small seed with a linear congruential generator, the seed value can be expanded to make a better initializer. Linear generators are slow in redistributing the bits in the seed value, so a small difference will cause the initial few values to be close to each other.

The standard library provides std::seed_seq which will expand a small seed to a better initializer value:

seed_seq seed({t});
default_random_engine e(seed);
like image 1
jpa Avatar answered Nov 19 '22 12:11

jpa