Having the ability to generate random numbers in a program can be very useful, whether it's to make a simple number guessing game or to see how a program accepts a variety of possible inputs. For our purposes, a random number is a value which was not obvious before creation. And, any number in the availible range should be just as likely as any other, known as a uniform distribution.
There are at least two ways to accomplish this feat. For exploring the two examples below, I will use the following,
The Base Main. The function body of
RandomNumber( ) will be the only difference between the examples. We use the
rand( ) function which returns a number which is offset by a random seed. The seed is set by passing the
srand( ) function with a number, generally the time.
The Base Main |
main.cpp |
//===================================INCLUDES================================== #include <iostream> //Included
for text output, cout. #include <time.h> //Included to get current time.
//Assume namespace
::std. using namespace std;
//=============================FUNCTION PROTOTYPES=============================
int RandomNumber( int min, int max );
//==================================MAIN ENTRY=================================
int main( void )
{ //Initialize Rand( ) function's seed with current time. srand( static_cast<unsigned int>( time(NULL) ) );
//Loop for 100 times. for ( int i = 0; i < 100; i++ ) {
//Output a number
between 1 and 100. cout
<< RandomNumber( 1, 100 ) << endl; }
//End the program, returning
zero. return ( 0 );
}
//=============================FUNCTION DEFINITIONS============================
|
The above code seeds the randomization with the system time. When a function definition for
RandomNumber( ) is appended, 100 numbers are
generated to the screen.
The Modulus Method
Currently the popular implementation for generating pseudo-random numbers in C++,
The Modulus Method, is so widely used that not many people consider the process. Even though we can't employ actual randomness since most computers behave predictably, there's no reason we can't explore some different implimentations.
The Modulus Method |
main.cpp, RandomNumber( ) |
//=============================FUNCTION DEFINITIONS============================ //----------------------------------------------------------------------------- // Function: RandomNumber // Description: Returns a random number between min value and max value, // including min and max. [min, ..., max] //-----------------------------------------------------------------------------
int RandomNumber( int min, int max ) { //Return a random number between min and max, inclusive. // Adds minimum value to the
remainder of division of the number returned // from
Rand() function and the maximum number, plus one, less the minimum.
return ( min + rand( ) % ( (max + 1) - min ) ); } |
The random number is seeded by passing the time, an
unsigned
integer, into the
srand( ) function. This
ensures that the numbers returned from calls to
rand ( )
are potentially different each time the program runs.
The
rand( ) function returns a random integer which is between 0 and the
RAND_MAX, the constant specified by the library, which is guarenteed to at least be 32,767.
To get this number within a desired range, other than 0 to 32,767, the
modulus operator, %, is used to calculate the remainder of division between the number returned and the maximum value. A random number between 0 and 9, would use
rand( ) % 10. Any number returned by
rand ( ) is less than 10. For example, if
rand( ) returns 8000, the modulus operation would divide 8000 / 10, which evenly divides, so there would be a remainder of 0, which would be our random number result for that run.
In the RandomNumber function the following is used to specify the minimum and maximum range for the resulting random number:
( min + rand( ) % ( (max + 1) - min ) ); |
More simply, whatever the random number is, is then acted on by the modulus to clamp its upper bounds and then has the minimum value added to it to clamp its lower bounds. There's some repetition, albeit sporatic, to the remainder of numbers early on in the number system which isn't so in later numbers, which could sometimes makes the randomness less uniform than desired.
Modulus Step-by-step
To show how this works, consider if the minimum of 1 and maximum of 10 used.
RandomNumber(1, 10);The maximum desired number is increased by one to allow for the maximum number itself to possibly result.
( 1 + rand( ) % ( (10 + 1) - 1 ) ); |
The result, 11, is minused from the minimum desired number, in this case 1. This makes the number to the right of the modulus the equivalent of the desired range from zero.
( 1 + rand( ) % ( 11 - 1 ) ); |
Rand ( ) is ultimately a number, it is the driving force in getting a different result each time, so let's say that rand ( ) returns 4892, just a random number between 0 and
RAND_MAX.
The result of 4892 % 10 is 2.
The minimum desired value, 1, is then added to the resulting remainder, 2, to ensure it is at least the minimum value. This guarentees that the number will never be the same or higher than the number divided by. This should make sense, if you divide by a number you'll never get a remainder of that number.
The number returned by calling
RandomNumber(1, 10) in this case is 3.
The Do-While Method
Rather than using the somewhat limited remainder of division and then clamping down on whatever number results, we could instead call random until a number within our desired range comes about.
The Do-While Method |
main.cpp |
//=============================FUNCTION DEFINITIONS============================ //----------------------------------------------------------------------------- // Function: RandomNumber // Description: Returns a random number between min value and max value, // including min and max. [min, ..., max] //----------------------------------------------------------------------------- int RandomNumber ( int min, int max ) { int randomNumber;
do { //Set
randomNumber equal to a random number.
randomNumber = rand ( );
}
//Generate a new random number while randomNumber // is less than the minimum or more than
maximum. while (
randomNumber < min || randomNumber > max );
//Return the random number between, and including, min and max. return randomNumber; } |
In this code we create a local variable,
int randomNumber, to hold the different random numbers produced
by
rand( ), which is some amount between 0 and
RAND_MAX. Should the
randomNumber
generated by
rand( ) be outside the range,
randomNumber < min || randomNumber > max, we
do it again until it is within our desired
range. Essentially, it simply generates a random number until it gets one
within the range between
min and
max.
The distribution is more uniform than the
modulus method above, but the smaller the range the more time it takes to return a
number. This is something to consider if the program is very processor intensive
where millions of time-sensitive operations occur every second.
Random Number Generation Frequency
You might wonder if the distribution pattern actually skewed. In some cases, it is possible that certain numbers appear more frequently, though it's not apocalyptic, and users wont be able to predict with certainty the next number to be generated. However, to give you an idea of the distribution pattern for both methods, I'll use the code below to do a side-by-side comparison:
Generating a Frequency Report |
main.cpp |
//===================================INCLUDES================================== #include <iostream> //Included for text output, cout. #include <iomanip> //Included for output formatting manipulation.
#include <time.h> //Included to get current time.
//Assuming namespace ::std. using namespace std;
//=============================FUNCTION PROTOTYPES============================= int RandomNumber_Modulus( int min, int max );
int RandomNumber_DoWhile( int min, int max );
//==================================MAIN ENTRY================================= int main( void ) { const int RANGE_MINIMUM = 0; const int RANGE_MAXIMUM = 50; const int SAMPLE_SIZE = 9000;
//Declare frequency variables to zero. intmodulusFrequency[RANGE_MAXIMUM - RANGE_MINIMUM] = {0}; int dowhileFrequency[RANGE_MAXIMUM - RANGE_MINIMUM] = {0};
int modulusResult = 0; int dowhileResult = 0;
//Initialize Rand( ) function's seed with current time.
srand( static_cast<unsigned int>( time (NULL) ) );
//Generate
SAMPLE_SIZE count of random numbers per generation method. for ( int i = 0; i < SAMPLE_SIZE; i++ ) { //Set result variable to the random number from respective function.
modulusResult = RandomNumber_Modulus( RANGE_MINIMUM, RANGE_MAXIMUM ); dowhileResult = RandomNumber_DoWhile( RANGE_MINIMUM, RANGE_MAXIMUM );
//Increment frequency count for the number returned.
modulusFrequency[modulusResult]+
+; dowhileFrequency
[dowhileResult]++; }
//Output a table header cout << left << setw(16) << "RANGE_MINIMUM :" << setw(15) <<
RANGE_MINIMUM <<
setw(16) << "SAMPLE_SIZE :" << setw(15)
<< SAMPLE_SIZE << endl << setw(16) << "RANGE_MAXIMUM :" << setw(15) << RANGE_MAXIMUM << setw(16) << "RANGE COUNT :" << setw(15) <<
RANGE_MAXIMUM - RANGE_MINIMUM << endl << endl << setw(18) << "THE MODULUS METHOD" << setw(13) << ""
<< setw(19) << "THE DO-WHILE METHOD" <<
endl;
//Output the frequency
results. for ( int i = 0; i < (RANGE_MAXIMUM - RANGE_MINIMUM); i++ )
{ //Output the modulus results. cout << setw(3) << i << setw(9) << "occurred" << setw(4) << modulusFrequency[i] << setw(7) << "times.";
cout << setw(8) << "";
//Output the do-while results. cout << setw(3) << i << setw(9) << "occurred" << setw(4) << dowhileFrequency[i] << setw(7) << "times.";
cout << endl; }
//End the program, returning zero. return ( 0 ); }
//=============================FUNCTION DEFINITIONS============================ //----------------------------------------------------------------------------- // Function: RandomNumber_Modulus // Description: Returns a random number between min value and max value, // including min and max. [min, ..., max] //----------------------------------------------------------------------------- int RandomNumber_Modulus( int min, int max ) { //Return a random number between min and max, inclusive. // Adds minimum value to the remainder of division of the number returned // from Rand() function
and the maximum number, plus one, less the minimum. return ( min + rand( ) % ( (max + 1) - min ) ); }
//----------------------------------------------------------------------------- // Function: RandomNumber_DoWhile // Description: Returns a random number between min value and max value, // including min and max. [min, ..., max] //----------------------------------------------------------------------------- int RandomNumber_DoWhile( int min, int max ) { int randomNumber;
do { //Set randomNumber equal to a random number. randomNumber = rand ( ); } //Generate a new random number while randomNumber // is less than the minimum or more than maximum. while ( randomNumber < min || randomNumber > max );
//Return a random number between min and max, inclusive. return randomNumber; }
|
This code creates random
SAMPLE_SIZE count of numbers between
RANGE_MINIMUM and
RANGE_MAXIMUM using the modulus method,
RandomNumber_Modulus( ), and the do-while method,
RandomNumber_DoWhile( ). The modulus random number result,
modulusResult, is added to the element of the modulus frequency array,
modulusFrequency. The do-while random number,
dowhileResult, is added to the element
dowhileFrequency.
Then the program outputs to the screen, the count of each number that occurred for each method. This is a small range, but the purpose of a uniform number generator is to have all numbers within the range equally possible; there shouldn't be any one number occuring too often compared to the others.
Here's a sample output: