6

I heard some guys telling that the use of rand() is bad EVEN AFTER USING srand() to get a seed. Why is that so? I want to know how the stuff happens... And sorry for another question.. but what is an alternative to this then?

plasmacel
  • 7,355
  • 5
  • 42
  • 87
Sid
  • 147
  • 2
  • 12
  • You can use for example [std::random_device](https://en.cppreference.com/w/cpp/numeric/random/random_device) which can be used with many number distributions. – Rafał Górczewski Oct 18 '18 at 07:49
  • The seed changes when I use srand(time(NULL)) but still its use is not recommended. Why is that so? – Sid Oct 18 '18 at 07:59
  • 1
    this [video](https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) is a bit exaggerating the issues, but it explains quite well what are some problems with `rand()` – 463035818_is_not_a_number Oct 18 '18 at 08:00
  • @Sid `time(NULL)` changes every second. If you run it multiple times per second, you get the same results. – vll Oct 18 '18 at 08:04

6 Answers6

13

There are two parts to this story.

First, rand is a pseudorandom number generator. This means it depends on a seed. For a given seed it will always give the same sequence (assuming the same implementation). This makes it not suitable for certain applications where security is of a great concern. But this is not specific to rand. It's an issue with any pseudo-random generator. And there are most certainly a lot of classes of problems where a pseudo-random generator is acceptable. A true random generator has its own issues (efficiency, implementation, entropy) so for problems that are not security related most often a pseudo-random generator is used.

So you analyzed your problem and you conclude a pseudo-random generator is the solution. And here we arrive to the real troubles with the C random library (which includes rand and srand) that are specific to it and make it obsolete (a.k.a.: the reasons you should never use rand and the C random library).

  • One issue is that it has a global state (set by srand). This makes it impossible to use multiple random engines at the same time. It also greatly complicates multithreaded tasks.

  • The most visible problem of it is that it lacks a distribution engine: rand gives you a number in interval [0 RAND_MAX]. It is uniform in this interval, which means that each number in this interval has the same probability to appear. But most often you need a random number in a specific interval. Let's say [0, 1017]. A commonly (and naive) used formula is rand() % 1018. But the issue with this is that unless RAND_MAX is an exact multiple of 1018 you won't get an uniform distribution.

  • Another issue is the Quality of Implementation of rand. There are other answers here detailing this better than I could, so please read them.

In modern C++ you should definitely use the C++ library from <random> which comes with multiple random well-defined engines and various distributions for integer and floating point types.

Peter O.
  • 28,965
  • 14
  • 72
  • 87
bolov
  • 58,757
  • 13
  • 108
  • 182
  • Awesome! That was the answer I was looking for! – Sid Oct 18 '18 at 08:46
  • 4
    None of the PRNGs have a "distribution engine". A distribution takes raw random values from a PRNG and samples/transforms its values to fit a distribution. You can use any C++ random distribution with `rand()` if you write a wrapper functor class which mimics the interface of C++ PRNG types. – plasmacel Oct 18 '18 at 09:05
  • @plasmacel very true. I was mostly thinking to the C random library as a whole vs the C++11 random library as a whole, but that did not get into the written words :). I rephrased the post. Thank you, very good points. – bolov Oct 18 '18 at 09:41
  • The standard for C doesn't even specify a particular distribution that the "pseudo-random numbers" delivered by `rand` have to follow, including the uniform distribution. – Peter O. Jul 08 '20 at 22:28
6

None of the answers here explains the real reason of being rand() bad.

rand() is a pseudo-random number generator (PRNG), but this doesn't mean it must be bad. Actually, there are very good PRNGs, which are statistically hard or impossible to distinguish from true random numbers.

rand() is completely implementation defined, but historically it is implemented as a Linear Congruential Generator (LCG), which is usually a fast, but notoriously bad class of PRNGs. The lower bits of these generators have much lower statistical randomness than the higher bits and the generated numbers can produce visible lattice and/or planar structures (the best example of that is the famous RANDU PRNG). Some implementations try to reduce the lower bits problem by shifting the bits right by a pre-defined amount, however this kind of solution also reduces the range of the output.

Still, there are notable examples of excellent LCGs, like L'Ecuyer's 64 and 128 bits multiplicative linear congruential generators presented in Tables of Linear Congruential Generators of Different Sizes and Good Lattice Structure, Pierre L'Ecuyer, 1999.

The general rule of thumb is that don't trust rand(), use your own pseudo-random number generator which fits your needs and usage requirements.

plasmacel
  • 7,355
  • 5
  • 42
  • 87
4

What is bad about rand/srand is that rand

  • uses an unspecified algorithm for the sequence of numbers it generates, yet
  • allows that algorithm to be initialized with srand for repeatable "randomness".

These two points, taken together, hamper the ability of implementations to improve on rand's implementation (e.g., to use a cryptographic random number generator [RNG] or an otherwise "better" algorithm for producing pseudorandom numbers). For example, JavaScript's Math.random and FreeBSD's arc4random don't have this problem, since they don't allow applications to seed them for repeatable "randomness" — it's for exactly this reason that the V8 JavaScript engine was able to change its Math.random implementation to a variant of xorshift128+ while preserving backward compatibility. (On the other hand, letting applications supply additional data to supplement "randomness", as in BCryptGenRandom, is less problematic; even so, however, this is generally seen only in cryptographic RNGs.)

Also:

  • The fact that the algorithm and the seeding procedure for rand and srand are unspecified means that even reproducible "randomness" is not guaranteed between rand/srand implementations, between versions of the same standard library, between operating systems, etc.
  • If srand is not called before rand is, rand behaves similarly as though srand(1) were first called. In practice, this means that rand can only be implemented as a pseudorandom number generator (PRNG) rather than as a nondeterministic RNG, and that rand's PRNG algorithm can't differ in a given implementation whether the application calls srand or not.

EDIT (Jul. 8, 2020):

There is one more important thing that's bad about rand and srand. Nothing in the C standard for these functions specifies a particular distribution that the "pseudo-random numbers" delivered by rand have to follow, including the uniform distribution or even a distribution that approximates the uniform distribution. Contrast this with C++'s uniform_int_distribution and uniform_real_distribution classes, as well as the specific pseudorandom generator algorithms specified by C++, such as linear_congruential_engine and mt19937.

EDIT (Dec. 12, 2020):

Yet another bad thing about rand and srand: srand takes a seed that can only be as big as an unsigned int. In most mainstream C implementations today, unsigned int is 32 bits long, meaning that only 2^32 different sequences of numbers can be selected this way even if the underlying algorithm implemented by rand can produce many more different sequences than that (say, 2^128 or even 2^19937 as in C++'s mt19937).

Peter O.
  • 28,965
  • 14
  • 72
  • 87
2

Firstly, srand() doesn't get a seed, it sets a seed. Seeding is part of the use of any pseudo random number generator (PRNG). When seeded the sequence of numbers that the PRNG produces from that seed is strictly deterministic because (most?) computers have no means to generate true random numbers. Changing your PRNG won't stop the sequence from being repeatable from the seed and, indeed, this is a good thing because the ability to produce the same sequence of pseudo-random numbers is often useful.

So if all PRNGs share this feature with rand() why is rand() considered bad? Well, it comes down to the "psuedo" part of pseudo-random. We know that a PRNG can't be truly random but we want it to behave as close to a true random number generator as possible, and there are various tests that can be applied to check how similar a PRNG sequence is to a true random sequence. Although its implementation is unspecified by the standard, rand() in every commonly used compiler uses a very old method of generation suited for very weak hardware, and the results it produces fair poorly on these tests. Since this time many better random number generators have been created and it is best to choose one suited to your needs rather than relying on the poor quality one likely to provided by rand().

Which is suitable for your purposes depends on what you are doing, for example you may need cryptographic quality, or multi-dimensional generation, but for many uses where you simply want things to be fairly uniformly random, fast generation, and money is not on the line based on the quality of the results you likely want the xoroshiro128+ generator. Alternatively you could use one of the methods in C++'s <random> header but the generators offered are not state of the art, and much better is now available, however, they're still good enough for most purposes and quite convenient.

If money is on the line (e.g. for card shuffling in an online casino, etc.), or you need cryptogaphic quality, you need to carefully investigation appropriate generators and ensure they exactly much your specific needs.

Jack Aidley
  • 17,045
  • 6
  • 35
  • 66
  • I meant using srand to get a seed not that it got a seed. Sorry if I was unclear... – Sid Oct 18 '18 at 09:51
  • Re: "`rand()` uses a very old method of generation" -- there is no such requirement. – Pete Becker Oct 18 '18 at 12:43
  • @PeteBecker: There is no such requirement, no, but all commonly used C++ compilers _do_ use such methods. The standard is a silly way to talk about how C, C++, or any other language actually behaves. – Jack Aidley Oct 18 '18 at 12:58
  • @JackAidley -- on the contrary: sweeping generalizations (in particular, "all commonly used C++ compilers...) are a silly way to talk about how C, C++, or any other language behaves. If you want to make an accurate statement, put in the appropriate qualifiers. If you had said "all the compilers whose libraries I've examined (which is by far not all existing compilers) do ..." it would have been a different matter (assuming, of course, that you have actually made such a survey or can otherwise validate such a claim). – Pete Becker Oct 18 '18 at 13:23
  • @Jack Aidley: The same thing you meant by saying setting a seed. – Sid Oct 21 '18 at 14:07
0

rand is usually -but not always-, for historical reasons, a very bad pseudo-random number generator (PRNG). How bad is it is implementation specific.

C++11 has nice, much better, PRNGs. Use its <random> standard header. See notably std::uniform_int_distribution here which has a nice example above std::mersenne_twister_engine.

PRNGs are a very tricky subject. I know nothing about them, but I trust the experts.

Basile Starynkevitch
  • 1
  • 16
  • 251
  • 479
-4

If you use rand(), you will basically have the same result after generating your random number. So even after using srand(), it will be easy to predict the number generated if someone can guess the seed you use. This is because the function rand() uses a specific algorithm to produce such numbers

With some time to waste, you can figure out how to predict numbers generated by the function, given the seed. All you need now is to guess the seed. Some people refer to the seed as the current time. So if can guess the time at which you run the application, I ll be able to predict the number

IT IS BAD TO USE RAND()!!!!

PhoenixBlue
  • 833
  • 2
  • 8
  • 20
  • 3
    The algorithm is implementation defined. See https://stackoverflow.com/questions/1026327/what-common-algorithms-are-used-for-cs-rand – vll Oct 18 '18 at 08:33
  • The problem you specify is only a problem if you have specific needs for the generator. It is not a general problem with `rand()`. – Jack Aidley Oct 18 '18 at 08:34
  • 4
    Um, **every** pseudo-random number generator uses a specific algorithm to produce its results. Whether or not you can predict the next number depends on the details of the algorithm. Neither C nor C++ requires that `rand()` be implemented badly. – Pete Becker Oct 18 '18 at 12:41