7

I have a bash script that starts many client processes. These are AI game players that I'm using to test a game with many players, on the order of 400 connections.

The problem I'm having is that the AI player uses

srand( time(nullptr) );

But if all the players start at approximately the same time, they will frequently receive the same time() value, which will mean that they are all on the same rand() sequence.

Part of the testing process is to ensure that if lots of clients try to connect at approximately the same time, the server can handle it.

I had considered using something like

srand( (int) this );

Or similar, banking on the idea that each instance has a unique memory address.

Is there another better way?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Eyelash
  • 1,760
  • 2
  • 12
  • 18
  • 2
    [`std::random_device`](http://en.cppreference.com/w/cpp/numeric/random/random_device) – Igor Tandetnik Jun 03 '18 at 00:23
  • submit as a response please – Eyelash Jun 03 '18 at 00:24
  • Possible duplicate of https://stackoverflow.com/questions/45069219/how-to-succinctly-portably-and-thoroughly-seed-the-mt19937-prng – Galik Jun 03 '18 at 00:43
  • 2
    **WARNING**: Using [`rand()` is considered harmful](https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) and you’re strongly encouraged to use an appropriate [random number generator facility in the Standard Library](http://en.cppreference.com/w/cpp/numeric/random) that produces actually random values. Your use of `time(NULL)` as a random number seed means that this will produce identical results if run in the same second, and on many platforms `rand()` is [*barely* random at all](http://dilbert.com/strip/2001-10-25). – tadman Jun 03 '18 at 00:44
  • "they will frequently receive the same time" - or, they might even be able to [choose good starting times](https://taeb.github.io/2009/03/02/predicting-and-controlling-nethacks-randomness.html). – aschepler Jun 03 '18 at 00:47
  • @tadman, this is why i'm asking the question... many flavors of "random" exist, and the lazy way is just not good enough – Eyelash Jun 03 '18 at 07:03
  • It is a somewhat odd problem to have, humans are pretty good random number generators. If it is too noticeable then you don't want to use random numbers to seed the RNGs, it can't be random if no duplicates are allowed. The KISS approach is to simply increment the seed by 1 for each player. – Hans Passant Jun 03 '18 at 11:03

4 Answers4

3

Use a random seed to a pseudorandom generator.

std::random_device is expensive random data. (expensive as in slow) You use that to seed a prng algorithm. mt19937 is the last prng algorithm you will ever need.

You can optionally follow that up by feeding it through a distribution if your needs require it. i.e. if you need values in a certain range other than what the generator provides.

std::random_device rd;
std::mt19937 generator(rd());
Taekahn
  • 1,592
  • 1
  • 13
  • 16
  • 2
    expensive (on the order of milliseconds or microseconds) is acceptable if the result is correct, even if it's blocking in a multithreading context – Eyelash Jun 03 '18 at 07:14
  • 1
    "if the result is correct". Considering it would take a team of data scientists with PhDs to tell the difference between an actual random data sample and one generated from mt19937, i would ask why you wouldn't want fast, repeatable (if needed), and correct. The only reason (imo) to not use it is if you need a cryptographically secure algorithm. – Taekahn Jun 03 '18 at 12:29
3

These days rand() and srand() are obsolete.

The generally accepted method is to seed a pseudo random number generator from the std::random_device. On platforms that provide non-deterministic random sources the std::random_device is required to use them to provide high quality random numbers.

However it can be slow or even block while gathering enough entropy. For this reason it is generally only used to provide the seed.

A high quality but efficient random engine is the mersenne twister provided by the standard library:

inline
std::mt19937& random_generator()
{
    thread_local static std::mt19937 mt{std::random_device{}()};
    return mt;
}

template<typename Number>
Number random_number(Number from, Number to)
{
    static_assert(std::is_integral<Number>::value||std::is_floating_point<Number>::value,
        "Parameters must be integer or floating point numbers");

    using Distribution = typename std::conditional
    <
        std::is_integral<Number>::value,
        std::uniform_int_distribution<Number>,
        std::uniform_real_distribution<Number>
    >::type;

    thread_local static Distribution dist;

    return dist(random_generator(), typename Distribution::param_type{from, to});
}
Galik
  • 47,303
  • 4
  • 80
  • 117
2

You use a random number seed if and only if you want reproducible results. This can be handy for things like map generation where you want the map to be randomized, but you want it to be predictably random based on the seed.

For most cases you don't want that, you want actually random numbers, and the best way to do that is through the Standard Library generator functions:

#include <random>

std::random_device rd;
std::map<int, int> hist;
std::uniform_int_distribution<int> dist(0, 5);

int random_die_roll = dist(rd);

No seed is required nor recommended in this case. The "random device" goes about seeding the PRNG (pseudo random number generator) properly to ensure unpredictable results.

Again, DO NOT use srand(time(NULL)) because it's a very old, very bad method for initializing random numbers and it's highly predictable. Spinning through a million possible seeds to find matching output is trivial on modern computers.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • *"You use a random number seed if and only if you want reproducible results."* That seems a bit strong. While `std::random_device` is decently fast (at least on gcc on Linux), you would not run a Monte-Carlo simulation based solely on it, but rather use it to seed a Mersenne twister. – Baum mit Augen Jun 03 '18 at 00:58
  • @BaummitAugen You can feed that into other PRNG functions, as I'm showing here in this example adapted from the documentation. `random_device` can run out of "entropy" if you lean on it too hard. – tadman Jun 03 '18 at 00:59
  • I'm sorry, I don't quite understand your comment. Are disagreeing with something I wrote? If so, which part? – Baum mit Augen Jun 03 '18 at 01:01
  • That `std::random_device` is by design not predictable nor reproducible, but Mersenne twisters can be, if seeded with the same value. `std::random_device` is good for getting random seeds, and although it can produce random data as well, it's less efficient than using a Mersenne twister or some other generator. – tadman Jun 03 '18 at 01:03
  • 1
    Then fix your answer; that (speed) is another reason to use a seeded prng. Maybe seeded from a random device, but seeded. – Yakk - Adam Nevraumont Jun 03 '18 at 01:17
  • 1
    `std::random_device` can be deterministic if the implementation doesn't have features (or exploit hardware features) that achieve non-determinism. In such a case, it is effectively the same as using a seed. Something worth checking (e.g. reading documentation for your compiler/library) if you really need unpredictability. – Peter Jun 03 '18 at 02:09
  • @Peter Fair enough. – tadman Jun 03 '18 at 03:02
  • do we know the root seed of std::random_device ? If different processes called from bash happen in the same microsecond, does it return different values? For this game, it's easy enough to test, but deeper understanding is always good, What is the root source of std::random_device ? – Eyelash Jun 03 '18 at 07:20
  • It's going to be PRNG one way or another. – Eyelash Jun 03 '18 at 07:25
  • 1
    You talk about `std::random_device` seeding the `PRNG` but there is no `PRNG` in your code. Also this code is expensive and may even block while the device gathers more entropy. I don't see this as a good general solution which would be to seed a `PRNG` with the `std::random_device` rather than use it directly. – Galik Jun 03 '18 at 09:25
  • 1
    Thank you everyone... I love the Mersenne numbers, and didn't notice this chunk of the std lib. Very helpful! Thank you again, it works perfectly. – Eyelash Jun 03 '18 at 16:46
-1

I'm trying so seed the random function with errno :

#include <stddef.h>
#include <string.h>
int main(void){
    srand(&errno);
    srand(strerror(0));
    return rand();
}
Michel
  • 259
  • 2
  • 3