5

I don't understand why srand() generates so similar random numbers between runs!

I am trying to run the following code

srand ( time(NULL) );
int x = rand();
cout << x << endl;

However instead of a proper random number I always end up with almost the same number, which is growing slowly as the time goes. So I get numbers like: 11669, 11685, 11701, 11714, 11731.

What am I doing wrong?

I am using Visual Studio 2010 SP1.

OK, is srand() really that simple? I mean how would anyone call it a random function?

srand(1) => rand() = 41
srand(2) => rand() = 45
srand(3) => rand() = 48
srand(4) => rand() = 51
....
hyperknot
  • 13,454
  • 24
  • 98
  • 153

8 Answers8

11

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 1
    I ran into this problem with production code five or so years ago for the Windows version of our product. The solution was to eat the first rand() call after seeding, for Windows only. I ran extensive analysis (billions of samples) for Windows, AIX and Linux, and got acceptable, similar results among all three test environments. I also took the time to use a better seed value (current time in milliseconds % 32000 + pid of the process, something like that). – Code4aliving Nov 04 '15 at 22:46
7

Make sure you're doing

srand ( time(NULL) );
while(condition) {
    int x = rand();
    cout << x << endl;
}

and not

while(condition) {
    srand ( time(NULL) );
    int x = rand();
    cout << x << endl;
}

The first way the seed is changed every iteration. The second way you are performing the random function on a very similar seed each iteration (because time doesn't change much).

Kyle
  • 1,978
  • 1
  • 18
  • 30
  • 1
    The reason why the latter won't work is because the current time doesn't update often enough, so many iterations will read the same time and therefore use the same seed. – hammar Jul 12 '11 at 17:24
  • I am doing it like this. int main(int argc, char* argv[]) { srand ( time(NULL) ); ..... – hyperknot Jul 12 '11 at 17:25
  • 1
    That code doesnt print out multiple results. You've given us a series of numbers but the only code you've shown prints out a single number. Also, the theoretical `while` could just be multiple runs of the program. – Kyle Jul 12 '11 at 17:29
3

If you are trying to run the program quickly in succession and get different random numbers each time, initializing with the current time is the wrong approach. What you need is a source of entropy; this question might get you started. Replacing time(NULL) with QueryPerformanceCounter() might be a good start, since it updates much more rapidly, but it's still somewhat predicatble - I don't know if that's important to you or not.

Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • It's nice to know about seeding with `QueryPerformanceCounter()` – Kyle Jul 12 '11 at 17:41
  • @Kyle, unfortunately QueryPerformanceCounter is Windows only. Linux has a much better solution with /dev/random, although it involves opening and closing a file. – Mark Ransom Jul 12 '11 at 17:47
  • 2
    @Mark `time` seems to be about the best portable solution, but most systems have a lot of various values which can be used. For a long time, I used to use a hash of `time`, the process id and the machine IP address, so that two people starting the program at the same time would still get completely different sequences. (Of course, `/dev/random` is even better, and is what I use under Unix today.) – James Kanze Jul 12 '11 at 17:52
  • I made a snippet using QueryPerformanceCounter() here: http://pastebin.com/DGxZZuRN – hyperknot Jul 12 '11 at 19:15
3

Since you have Visual Studio 2010, you can use the portable random device interface from modern C++ instead of time() to seed srand():

#include <iostream>
#include <random>
#include <cstdlib>
int main()
{
    std::random_device rd;
    std::srand(rd());
    std::cout << std::rand() << '\n';
}

Now running the program repeatedly will still produce different values. The same code will work with GNU g++ on Linux or any other modern compiler.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
2

OK, all credits go for Mark Ransom for his answer for explaining actually what is happening. I did not find source code in his linked question, so I googled it and found this, what is perfectly working on Windows. So for srand on windows, here is the source code to generate better srand() seed.

#include <windows.h>

int main()
{
  LARGE_INTEGER cicles;

  QueryPerformanceCounter(&cicles);
  srand (cicles.QuadPart);

  return 0;
}
hyperknot
  • 13,454
  • 24
  • 98
  • 153
1

I just had the same problem. The seeds were too similar even after tens of seconds. Since I get my numbers in this fashion:

int FlRandomInt(int LowerLimit, int UpperLimit)
{
 int Result;
 Result = rand();
 Result=LowerLimit+Result*(UpperLimit-LowerLimit)/RAND_MAX;

 return Result;
}

which I know is not the best way to go for integers, but I use the same procedure to generate random floats and doubles, so it's good to verify if those are significantly different, instead of just at the last decimals.

Anyway just wanted to post a solution that works fine for me. It's simply multiplying the time seed by 100:

srand(( unsigned )time( 0 ) * 100 );

Hope it helps, even if I'm sure there are more elegant ways around the problem.

Fledi
  • 11
  • 1
  • Call a dummy rand(); after srand(); to make it more unique. Or use the code from my answer: http://stackoverflow.com/questions/6668282/srandtimenull-generating-similar-results/6668542#6668542 – hyperknot Aug 09 '11 at 03:43
0
#include"stdio.h" //rmv coding for randam number access using c++

 #include"conio.h"

 #include"time.h"

void main()

{

time_t t;

int i;

srand(time(null));

for(i=1;i<=10;i++)

cout<<(unsigned)rand()%100-90<<"\t";

for(i=1;i<=10;i++)

cout<<(char)rand()%100-90<<"\t";


getch();

}
laaposto
  • 11,835
  • 15
  • 54
  • 71
0

From @James Kanze's test it seems that it is a peculiarity of VC++'s C runtime (though I am certain other libraries suffer in the same way). This library also suffers from having a minimum allowable RAND_MAX, but that's another issue.

The solution to the low variance of the initial value is simply to discard it:

void seed_rand( unsigned int seed )
{
    srand( seed ) ;
    (void)rand() ;
}

int main()
{
    seed_rand( time( NULL ) );
    int r1 = rand();
    std::cout << r1 << ' ' << rand() << std::endl;
    return 0;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165