Srand(Time(Null)) Generating Similar Results

srand(time(NULL)) generating similar results

First, srand() isn't a random function; it sets up the starting point
of a pseudo-random sequence. And somewhat surprisingly, your
implementation of rand() seems to be returning a value based on the
previous state, and not on the newly calculated state, so that the first
value after a call to srand() depends very much on the value passed to
srand(). If you were to write:

srand( time( NULL ) );
rand();
std::cout << rand() << std::endl;

, I'm sure you'll see a lot more difference.

FWIW: I tried the following on both Windows and Linux:

int
main()
{
srand( time( NULL ) );
int r1 = rand();
std::cout << r1 << ' ' << rand() << std::endl;
return 0;
}

Invoked 10 times at a one second interval, I got:

16391 14979
16394 25727
16397 3708
16404 25205
16407 3185
16410 13933
16417 2662
16420 13411
16427 2139

with VC++ under Windows—you'll note the very low variance of the
first call to rand()—and

1256800221 286343522
955907524 101665620
1731118607 991002476
1428701871 807009391
44395298 1688573463
817243457 1506183315
507034261 1310184381
1278902902 54648487
2049484769 942368151
1749966544 1833343137

with g++ under Windows; in this case, even the first value read is
relatively random.

If you need a good random generator, you'll probably have to use one
from Boost; the standard doesn't say much about what algorithm should be
used, and implementations have varied enormously in quality.

rand() generating the same number – even with srand(time(NULL)) in my main!

The issue is that the random number generator is being seeded with a values that are very close together - each run of the program only changes the return value of time() by a small amount - maybe 1 second, maybe even none! The rather poor standard random number generator then uses these similar seed values to generate apparently identical initial random numbers. Basically, you need a better initial seed generator than time() and a better random number generator than rand().

The actual looping algorithm used is I think lifted from Accelerated C++ and is intended to produce a better spread of numbers over the required range than say using the mod operator would. But it can't compensate for always being (effectively) given the same seed.

rand function is giving me the same result at each run even when I called srand(time(NULL))

There are two fundamental problems with your code which, in combination, produce the curious result you're experiencing.

Almost anyone will warn you about the use of the rand() interface. Indeed, the Mac OS manpage itself starts with a warning:

$ man rand
NAME
rand, srand, sranddev, rand_r -- bad random number generator

Yep, it's a bad random number generator. Bad random number generators can be hard to seed, among other problems.

But speaking of seeding, here's another issue, perhaps less discussed but nonetheless important:
Do not use time(NULL) to seed your random number generator.

The linked answer goes into more detail about this, but the basic issue is simple: the value of time(NULL) changes infrequently (if frequently is measured in nanoseconds), and doesn't change much when it changes. So not only are you relying on the program to not be run very often (or at least less than once per second), you're also depending on the random number generator to produce radically different values from slightly different seeds. Perhaps a good random number generator would do that, but we've already established that rand() is a bad random number generator.

OK, that's all very general. The specific problem is somewhat interesting, at least for academic purposes (academic, since the practicial solution is always "use a better random number generator and seed it with a good random seed"). The precise problem here is that you're using rand() % 7.

That's a problem because what the Mac OS / FreeBSD implementation of rand() does is to multiply the seed by a multiple of 7. Because that product is reduced modulo 232 (which is not a multiple of 7), the value modulo 7 of the first random number produced by slowly incrementing seeds will eventually change, but it will have to wait until the amount of the overflow changes.

Here's a link to the code. The essence is in these three lines:

    hi = *ctx / 127773;
lo = *ctx % 127773;
x = 16807 * lo - 2836 * hi;

which, according to a comment, "compute[s] x = (7^5 * x) mod (2^31 - 1) without overflowing 31 bits." x is the value which will eventually be returned (modulo 232) and it is also the next seed. *ctx is the current seed.

16807 is, as the comment says, 75, which is obviously divisible by 7. And 2836 mod 7 is 1. So by the rules of modular arithmetic:

x mod 7 = (16807 * lo) mod 7 - (2836 * hi) mod 7
= 0 - hi mod 7

That value only depends on hi, which is seed / 127773. So hi changes exactly once every 127773 ticks. Since the result of time(NULL) is in seconds, that's one change in 127773 seconds, which is about a day and a half. So if you ran your program once a day, you'd notice that the first random number is sometimes the same as the previous day and sometimes one less. But you're running it quite a bit more often than that, even if you wait a few seconds between runs, so you just see the same first random number every time. Eventually it will tick down and then you'll see a series of 3s instead of 4s.

rand() produces the same result on each function call (with srand(time(0))

(Converting my comment to an answer).

For most applications, you'll only really want to seed rand once in the course of running a program. Seeding it multiple times requires you to get different random seeds, and it's easy to mess that up.

In your case, the time function usually returns something with resolution on the level of seconds (though this isn't actually required by the standard). As a result, if you call time twice within the same second, you might get back the same value. That would explain why you're getting duplicate values: you're seeding the randomizer with the same value twice and then immediately querying it for a random number.

The best solution to this is to just seed the randomizer once. Typically, you'd do that in main.

If you really do want to seed the randomizer multiple times, make sure that you're doing so using a seed that is going to be pretty much random. Otherwise, you risk something like this happening.

srand(time(NULL)) doesn't change seed value quick enough

srand(time(NULL)) should be run exactly once to intialise the PRNG. Do this in Main when the application starts.

Explanation:

A PRNG (Pseudo-Random Number Generator) generates a deterministic sequence of numbers dependent on the algorithm used. A given algorithm will always produce the same sequence from a given starting point (seed). If you don't explicitly seed the PRNG then it will usually start from the same default seed every time an application is run, resulting in the same sequence of numbers being used.

To fix this you need to seed the PRNG yourself with a different seed (to give a different sequence) each time the application is run. The usual approach is to use time(NULL) which sets the seed based on the current time. As long as you don't start two instances of the application within a second of each other, you'll be guaranteed a different random sequence.

There's no need to seed the sequence each time you want a new random number. And I'm not sure about this, but I have the feeling that depending on the PRNG algorithm being used re-seeding for every new number may actually result in lower randomness in the resulting sequence.

Why is srand(time(NULL)) working smoothly even though I repeatedly reset it?

According to standard the use of std::rand in both std::random_shuffle and std::shuffle is implementation-defined (though it is often the case that an std::rand is used this is not guaranteed). Try it on another compiler? Another platform?

If you want to make sure the std::rand is used you should let your code use it explicitly (for example, using lambda expression):

random_shuffle(perm.begin(), perm.end(), []{return std::rand();});

On a somewhat unrelated note, the time()'s precision is one whole second, your code runs way faster than that (I would hope) so those multiple calls to srand() result in resetting to the same-ish seed



Related Topics



Leave a reply



Submit