// random.cc: random-number facilities for the CSC 270 simulation example // -- J. Clarke, March-June 1996 #include "random.h" #include // This file defines separate streams of random numbers to be used // to ensure independent features of the "world" are modelled // independently in simulation programs. Unfortunately Standard C // only provides one random-number generating function, rand(). // This function does not allow (as far as I can see) independent // streams; the best this module can do if rand() must be used is // to make your program *look* as if there are real separate streams. // // However, the rand48() facility, while not part of Standard C, is // a useful extension available at CDF, and it allows construction // of independent streams. To select the rand48 package, make sure // following #define is in effect; to unselect it (and use rand()), // just comment it out. #define RAND48 // Both rand() and the rand48() facilities are defined in stdlib.h. // However, if you tell the compiler you're using Standard C (for // example, with gcc's -ansi flag), then the rand48 definitions are // omitted. To include them, __EXTENSIONS__ must be defined, and that's // the purpose of the following conditional definition. No change is // needed here if RAND48 is undefined. // // WARNING: You really shouldn't mess with compiler controls with // names beginning "__"! #ifdef RAND48 #define __EXTENSIONS__ #endif // And now, at last, we're ready for stdlib! #include // Initialize the stream of random numbers with the seed S. void randStream::setseed (unsigned long S) // CHANGE: Argument type changed from unsigned to unsigned long, July 4/96. // Pointed out by Angus Stewart. { #ifdef RAND48 xsubi[0] = 0; xsubi[1] = (unsigned short) (S >> (CHAR_BIT * sizeof (unsigned short))); // CHANGE: "CHAR_BIT *" added, July 4/96. // Pointed out by Angus Stewart. xsubi[2] = (unsigned short) S; #else // The standard random-number facility provides only one // stream. We will get along with just one stream. srand (S); #endif } // Create a stream starting with the seed provided. // Initialize the stream of random numbers. randStream::randStream (unsigned seed) { setseed (seed); } // Create a stream with an arbitrary starting seed. // Uses 1 as seed, since that's what rand() does. randStream::randStream (void) { setseed (1); } // Return the next random number in this stream, unconverted. long randStream::nextraw (void) { #ifdef RAND48 return nrand48 (xsubi); #else return (long) rand (); #endif } // Returns the next random number in this stream, converted to a double. // The result is in the range 0 <= result < 1. // Reference: E. Roberts, "Art & Science of C", p. 274. double randStream::nextfloat (void) { #ifdef RAND48 return erand48 (xsubi); #else return ((double) rand())/((double) RAND_MAX + 1); #endif } // Returns the next random number in this stream, converted to a long. // The result is in the range lo <= result <= hi. // Reference: E. Roberts, "Art & Science of C", p. 274. long randStream::nextint (long lo, long hi) { double d = nextfloat (); return lo + (long) (d * (hi - lo + 1)); } // Randomization is trickier than you'd think. We need something "really // random" to provide a seed, and the obvious things are the time of day // and the process number. The "really random" thing must give DIFFERENT // seeds--that is, unrelated seeds--for each stream that we try to // randomize. // // On a PC, the process number might not mean anything, and on any system, // the time of day might be the the same from one call to randomize to the // next, because the time elapsed between calls might be insignificant. // So we can only have ONE call to the "really random" source, and we have // to use it to provide many randomizing seeds for different streams. // // My solution, about which I'd be happy to hear comments, especially if // they are convincing improvements, is to have a "randomizing stream." // On the first call to randomize, this stream is initialized from the // time of day. Subsequent calls can't just use the value returned by // the randomizing stream, because then the streams randomized would // start out simply one step apart in the same sequence. We need to // use the random values returned to determine some kind of offset, // rather than using them "raw." I'm picking square root to do that. // // I'm not too happy about this, but at least square root is an independent // pseudo-random process from the one use by rand() and the rand48()'s. // For now. // // Finally, note that this is all much ado about nothing if we're using // the standard rand() facility, which provides only one stream. In that // situation, the calling program should make sure to do all its // randomization of the "different streams" before beginning real use // of the random numbers. #include #include // for sqrt // Pick starting seeds out of the circumambient blackness. void randomize (randStream * Stream) { // Randomizer is static, so its seed is only taken from "time" once. static randStream Randomizer ((long) time ((time_t *) NULL)); long S = (long) sqrt (Randomizer.nextraw ()); Stream -> setseed (S); }