2

What kind of approach is the best at generating a random 2-digit number (using only the standard C++ library)? These 2 seem like the most obvious, but I want to know which one is better and if there is a better one.

//1
#include <iostream>
#include <cstdlib>
#include <ctime>
int main()
{
     std::srand(std::time(NULL));
     std::cout << "Random value: " << std::rand() % 100;
     return 0;
}

//2
#include <iostream>
#include <random>
int main()
{
     std::random_device rnd;
     std::cout << "Random value: " << rnd() % 100;
     return 0;
}
DarkAtom
  • 2,589
  • 1
  • 11
  • 27
  • 1
    `uniform_int_distribution` is the best: https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution. Your current solutions don't actually produce uniformly random number. –  Apr 16 '19 at 23:21
  • It depends on what kind of distribution you want to have for these generated 2-digit numbers, like uniformly distributed, normally distributed, etc. – dhanushka Apr 17 '19 at 01:03

1 Answers1

4

Neither of those is very good. You want a less predictable seed (which srand/rand can't do), and a uniform distribution (which % 100 of just about anything won't do).

Instead, use the C++11 standard <random> PRNG suite (adapted from cppreference example code):

int main()
{
    // Seed with a real random value, if available
    std::random_device r;

    // Seed the default ending from good random
    std::default_random_engine eng(r());

    // Define a proper distribution that won't exhibit bias
    std::uniform_int_distribution<int> dist(0, 99);

    std::cout << "Random value: " << dist(eng) << std::endl;
}

Technically, the standard is a bit weak on std::random_device and allows it to be non-cryptographically random, but on all major OSes it should be decent. You're welcome to xor r() with time(NULL) if you want to ensure it's at least non-repeating, though it won't guarantee good randomness.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • 1
    I don't get it. Why is it better to use `random_device` to generate the seed rather than the number itself? – DarkAtom Apr 16 '19 at 23:25
  • @DarkAtom: If you're only generating one or two numbers, go ahead and use `random_device` directly (`dist(r)` will do the trick in the above code, removing the need for `eng`), but if you need more, faster, seeding a PRNG and using that is much faster. Note that seeding it properly is [somewhat tricky, so read up on making `seed_seq`s for seeding if you need really good seeding](https://stackoverflow.com/q/47562404/364696). – ShadowRanger Apr 16 '19 at 23:30
  • _You're welcome to xor r() with time(NULL)_ - unless first invocation of `r()` returns `time(NULL)` – Roman Apr 16 '19 at 23:36
  • @Roman: True. I suppose addition would be somewhat safer if you're worried about bad `random_device`, in that worst case is `time(NULL) * 2` rather than `0`. Downside is that I believe it introduces predictable patterns into good random values (due to the carry), where xor of a good value and a bad one remains good. Still can't believe the standard allowed `std::random_device` to be implemented so poorly without even a diagnostic output to warn you. – ShadowRanger Apr 16 '19 at 23:44