Pete Becker is Senior QA Project Manager for C++ at Borland International. He has been involved with C++ as a developer and manager at Borland for the past six years, and is Borland's principal representative to the ANSI/ISO C++ standardization committee.
Q
I'm using the rand function to generate a random number in one of my programs and it keeps generating the same number! I've tried using rand in a simplified version of my original program and it does the same thing. Here's a simplified version:
#include <stdio.h> #include <stdlib. h> main() { int a; a = rand(); printf( "Number: %d\n", a ); return 0; }What am I doing wrong? Any help would be greatly appreciated.A
I compiled this program with Borland's compiler. Every time I run it I get this result:
Number: 346If you build the program with a different compiler you will probably get a different value, but it will be the same every time you run that program. Let's take this a step further, and print out a sequence of values generated with rand:
#include <stdio.h> #include <stdlib.h> main() { int i; for( i = 0; i < 10; i++) { int a; a = rand(); printf( "Number: %d\n", a ); } return 0; }This program produces the output:
Number: 346 Number: 130 Number: 10982 Number: 1090 Number: 11656 Number: 7117 Number: 17595 Number: 6415 Number: 22948 Number: 31126This looks like a reasonably well mixed up sequence of numbers, but the program still produces the same sequence of numbers every time it is run. Your adventure game will be pretty boring if it presents exactly the same sequence of problems every time you play it.To decide whether this behavior is correct we start with a description of what rand is supposed to do. This description appears in the ANSI C standard in section 7.10.2.1:
Description: The rand function computes a sequence of pseudo-random integers in the range 0 to RAND_MAX. The implementation shall behave as if no library function calls the rand function.
Returns: The rand function returns a pseudo-random integer.
Now, if you don't know what a "pseudo-random integer" is, that's not much help. If you read on, though, you come to section 7.10.2.2, which defines the srand function:
#include <stdlib.h> void srand( unsigned int seed );Description: The srand function uses the argument as a seed for a new sequence of pseudo-random numbers to be returned by subsequent calls to rand. If srand is then called with the same seed value, the sequence of pseudo-random numbers shall be repeated. If rand is called before any calls to srand have been made, the same sequence shall be generated as when srand is first called with a seed value of 1.That's a lot of dense text, but understanding a language definition requires being able to read this sort of thing. For our purposes, the first sentence is the important part: it says that you get the same sequence for any initial seed. To get a different sequence you can call srand with a different seed, like this:
#include <stdio.h> #include <stdlib.h> int main( int argc, char *argv[] ) { int i; int seed; if( argc != 1 ) { seed = atoi( argv[1] ); srand( seed ); } for( i : 0; i < 10; i++ ) { int a; a = rand(); printf( "Number: %d\n", a ); } return 0; }If you run this program with no command-line arguments it will not call srand. In this case, it should produce the same result as the previous version. However, if you do give it a command-line argument, it will use the value of its argument as the parameter to srand. This should produce a different sequence of numbers. Table 1 shows my results for several different seed values. This table reveals several interesting points. First, the sequences for no seed and for a seed of 1 are the same. That's what the definition of srand requires. Second, the first value generated for each seed is 346 times the value of the seed. That's not required by the language definition, although it wouldn't surprise me if most compilers act similarly. We'll see why a little later. Third, despite the relationship between the seed value and the first value generated by rand, subsequent values don't display any straightforward relationship. In fact, that makes a good working definition of a pseudo-random sequence: there is no apparent relationship between adjacent numbers in the sequence.To get back to the original question, if we want to get a different value from the first call to rand in a program, we need to use srand to tell rand to generate a different sequence. This is often done by calling time to get a seed value. Using time assures that srand is called with a different value every time the program is run. Now, our sample program looks like this:
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int i; srand(time(NULL)); for( i : 0; i < 10; i++ ) { int a; a = rand(); printf( "Number: %d\n", a ); } return 0 ; }If you run this program several times in succession, you will see that it produces a different sequence of numbers each time.By now you may be asking yourself why the Standard specifies this behavior. Wouldn't it be easier to simply require rand to do this sort of initialization, so that it produced a different sequence of numbers every time? The answer is simple: debugging. If your program isn't working right, you have to stabilize it before you can figure out what's wrong. That means setting up an environment and a set of input conditions that reproduce the problem consistently. Once that's done you can step through the code with a debugger and find out where it's going wrong. If rand produced a different sequence every time you wouldn't be able to stabilize the program. Defects that showed up only under certain circumstances would come and go, depending on what value rand happened to start with. Rather than inflict this uncertainty on programmers, the language definition makes the behavior of rand consistent from run to run, and controllable through srand.
When I quoted the definition of srand from the ANSI C standard earlier I left part of it out. Here's the missing part:
Example
The following functions define a portable implementation of rand and srand.
static unsigned long int next = 1; int rand( void ) /* RAND_MAX assumed to be 32767 */ { next = next * 1103515245 + 12345; return (unsigned int) (next/65536) % 32768; } void srand( unsigned int seed ) { next = seed; }This implementation of rand uses the linear congruential method for generating the pseudo-random sequence. In the linear congruential method, the next value in the sequence is generated by multiplying the current value by some number (in this case 1,103,515,245) and incrementing the result by some other number (in this case 12,345), ignoring any overflows. A fair amount of science goes into choosing appropriate values for the multiplier and increment to ensure that the sequence of numbers generated won't repeat for a long time. If you want to know more about this method, or about pseudo-random numbers in general, the best source (as usual) is Donald Knuth's The Art of Computer Programming. Random numbers are discussed in Volume 2, "Seminumerical Algorithms," pages 1-177. [Also see "Quick and Portable Random Number Generators," CUJ, June 1995, p. 33 mb]To ask Pete a question about C or C++, send e-mail to pbecker@wpo.borland.com, use subject line: Questions and Answers, or write to Pete Becker, C/C++ Users Journal, 1601 W. 23rd St., Ste. 200, Lawrence, KS 66046.