Generating Random numbers
Learn random numbers generation.
We'll cover the following
Generating random numbers is an important step in many practical applications like science, arts, statistics, cryptography, gaming, and many other fields. Let’s see how we can generate random numbers in C++ using the rand()
function.
The rand()
function
The rand()
is a built-in function of C++ that returns a pseudo-random integral number in the range of 0
to RAND_MAX
(which is equal to the maximum value the int
data type can hold, i.e., 2147483647
).
Let’s write our program and call the rand()
function three times and see the output:
#include<iostream>#include<cstdlib>#include<ctime>using namespace std;int main(){cout << rand() << endl;cout << rand() << endl;cout << rand() << endl;return 0;}
-
Line 2:
#include<cstdlib>
is a standard library used for therand()
function. -
Lines 7–9: We display random numbers on the console by calling the
rand()
function three times.
Instruction: Run the above program three times and check the output. You should notice a problem. Look at the output carefully.
You must have noticed that there is always the same output when we run the above program:
1804289383
846930886
1681692777
We will discuss in a while why this happens. However, first let’s understand the functionality behind this rand()
function.
The working of the rand()
function
The rand()
function has several types of implementation. One of the implementations (which we will present) works using polynomials. Let’s recall what a polynomial is.
Polynomial
A polynomial is a mathematical function of the following format:
where is the variable on which the function is dependent. Let’s take an example of a polynomial with the following:
Evaluation of a polynomial
We can evaluate the polynomial function on a certain value of by putting at every place where the variable is in the function . For example, the above function at will be as follows:
The rand()
function usually holds a polynomial in the background, usually made up of prime number coefficients.
Let’s take the above polynomial as our sample polynomial and make our own random function. We’ll call it the custom_rand()
function.
int f(int x){int ans = 3 + (5* pow(x,1)) + (7*pow(x,2)); // Polynomial functionreturn ans;}// A global variable s, we call it seed variable with 0 as initial valueint s = 0;int custom_rand(){return s = f(s); // Update the seed variable with return value}
When we call custom_rand()
for the first time, f(0)
is called and evaluates to 3
(because ). Not only that, s
will now be holding the value 3
before returning. Now, when we call custom_rand()
again, it will call f(3)
and s
will have the next value as (because ). So the next random number is 81
and s
holds the value 81
, and so on. Every time we call rand_custom()
, the chain of numbers will continue.
We call the variable s
as the seed. A seed is basically the value at which the custom_rand()
function calls for evaluating its polynomial.
In each
custom_rand()
call, theseed
variable will be updated with the last returned value.
Let’s see the implementation in the playground below:
#include<iostream>#include<math.h>using namespace std;int f(int x){int ans = 3 + (5* pow(x,1)) + (7*pow(x,2)); // Polynomial functionreturn ans;}// A global variable s, we call it seed variable with 0 as initial valueint s = 0;int custom_rand(){return s = f(s); // Update the seed variable with return value}int main(){cout<<custom_rand()<<endl;cout<<custom_rand()<<endl;cout<<custom_rand()<<endl;cout<<custom_rand()<<endl; // overflow is happening}
There are two problems with the custom_rand()
function.
-
Notice that the sequence is increasing very fast. Very soon, the generated number exceeds the limit of the integer and causes overflow (this is why you will see the negative large number on the fourth
custom_rand()
call). -
Run the above program three times and note the output. Each run generates the same sequence of the three numbers.
Fixing the overflow problem
Fixing the overflow problem is quite easy; what we can do is some wrapping by taking the modulus with a very large prime number (usually, it is one more than the maximum value you want to have). For example, one solution could be this:
#include<iostream>#include<math.h>using namespace std;long long f(int x){ // the type "int" is replaced by 8 byte long long so that atleast// overflow can be avoided on the boundary caselong long ans = 3 + (5* pow(x,1)) + (7*pow(x,2)); // Polynomial functionreturn ans;}int s = 0;int custom_rand(){return s = f(s) % 100019; // Wrapping by taking modulus % from 100019.}int main(){cout<<custom_rand()<<endl;cout<<custom_rand()<<endl;cout<<custom_rand()<<endl;cout<<custom_rand()<<endl;cout<<custom_rand()<<endl;return 0;}
Fixing the repeated pattern
Whenever we execute the program again, the seed variable s
has the value 0
. Therefore, the chain of random numbers will be exactly the same.
How can we solve this problem? Think for a while.
Instruction: Can we somehow change the value of the seed s
every time the program starts?
Every time we start the program, we preferably want to initialize
s
with a random number. However, aren’t we stuck in a loop then? To generate a random number, we need another random number?
The srand(int seed)
function
In C++, we can have access to the seed s
, that global variable, by the use of the srand(int)
function, which takes an integer value as a parameter and assigns the value to the seed s
.
As we are making our own random function, let’s, for the sake of avoiding conflict, make the function custom_srand(int)
. Our implementation will be as follows:
...int f(int x)...// A global variable s, we call it seed variable with 0 as initial valueint s = 0;void custom_srand(int seed){s = seed; // Update the seed s with the new value}int custom_rand()...int main(){custom_srand(5);cout<<custom_rand()<<endl;..}
Let’s write some code and see the output:
#include<iostream>#include<math.h>using namespace std;long long f(int x){long long ans= 3 + (5* pow(x,1)) + (7*pow(x,2)); // Polynomial functionreturn ans;}// A global variable s, we call it seed variable with 0 as initial valueint s = 0;void custom_srand(int seed){s = seed; // Update the seed s with the new value}int custom_rand(){return s = f(s)%100019; // Update the seed variable with return value}int main(){int seed;cin >> seed; // Enter some value e.g. 0 1 2 1 2custom_srand( seed ) ;cout<<custom_rand()<<" "<<custom_rand()<<" "<<custom_rand()<<endl;}
Enter the input below
Task: Execute the above program at least five times with the following values of the seed as input. Write the output of the program on paper and observe:
Input Seed: 0
Input Seed: 1
Input Seed: 2
Input Seed: 1
Input Seed: 2
Can you see a problem?
Question: Why is this type of input-driven seed problematic?
Let’s say we wrote code for a game in which dice are used. The game could be Ludo or Snakes and Ladders. What if one team already knows on which seed they will yield a maximum number of sixes? They will definitely choose that seed and easily win every time, resulting in cheating.
How can we avoid this control of sequence generation by the end user?
The time(0)
function
The C/C++ library has a time()
function (which you can think of as int time(0)
) that returns the time in seconds passed since the Epoch (00:00:00 UTC, January 1, 1970).
After every second, this value gets incremented by 1
; this value is usually hardcoded in every computing machine.
Let’s see how we can use the time(0)
function in the following playground.
#include <iostream>#include<cstdlib>#include<ctime>using namespace std;int main() {cout << "Seconds Passed since Jan 01 1970 UTC: "<<time(0)<<endl;return 0;}
Task: Execute the above playground multiple times and note the last three digits of the seconds. You should see the seconds changing.
We can use the time(0)
function in various applications while writing programs. For example, we can use it to check the execution time from the start of a program till the end of the program.
For example,
-
If we want to check the execution time from the start of the program till the end of the program? Think about using the
time(0)
function. -
How can we add the delay in a program?
Application 1: Making the Sleep()
function
Let’s use time(0)
to make the wait_sec()
function. This function is also available in standard C++ with the name Sleep()
, defined in the library unistd.h
.
-
This function will take an integer
sec
, which we’ll use to create a pause of that many seconds. -
We will store the present time in
start_Time
using thetime(0)
function. -
Then
time(0) - start_Time
will calculate how much time has passed since the execution of the function started. -
If
time(0) - start_Time < sec
, we can put the execution in a spin loop; therefore, the loop will break only oncesec
seconds are consumed.
Let’s see the implementation in the following playground:
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; void wait_sec(int sec); int main() { int sec=0; cout << "After how many seconds you want to terminate the program : " ; cin >> sec ; cout << "Program starts ...!! " << "\n" ; wait_sec( sec ) ; cout << "Program terminated ...!! " << "\n"; return 0; } void wait_sec(int sec) { int Start_Time=time(0); while( (time(0) - Start_Time) < sec ) { } }
Instruction: Run the above program for inputs 2
, 3
, and 4
and note the delay time.
You can also use the wait_sec()
function to add the delay in any application or program.
Application 2: Measuring the time of execution in seconds
Now, if we want to check the execution time of the specific program in a number of seconds, we have the following.
- Idea: We will call the
time(0)
function at the start and end of the program. While calling these functions, we will also store these two times inint
variables. In the end, we will simply subtract the end time from the start time to get the execution time.
#include <iostream>#include<cstdlib>#include<ctime>using namespace std;int main() {int start_time = time(0); // Store the start timefor(int i=1; i < 2000000000; i++); // Add the delayint end_time = time(0); // Store the end timecout<<"Execution Time: "<<end_time - start_time<<" seconds"<<endl;}
Line 7 in the code above adds delay. Note that the
for
loop does not have a body, but it’s still valid. There is an empty statement (;
) following thefor
loop statement block, so no action is performed within the loop. As a result, the loop simply runs and takes up processing time, causing a delay in the program execution. Thefor
loop is executed, and it adds delay by incrementingi
tilli
is equal to 1999999999.
Let’s now discuss how time(0)
can help us generate random numbers.
What will srand(time(0))
do?
Time changes every second, hence the time(0)
value changes every second. Can we use this point to our advantage?
Question: What will happen if we write srand(time(0))
in the first line of int main()
or anywhere else in the code?
It will initialize the seed with the current time value, which will always differ when we start our program. This way, we will never get the same sequence at every execution time because the time function will not give us the same time as the seed, resulting in a different generated sequence.
For using
time(0)
, we have to add a prototype#include<ctime>
or#include<time.h>
at the start of the code.It is recommended that the
srand(time(0))
instruction is on the first line ofmain()
. Never usesrand(time(0))
again during the program.
Let’s write the complete code and execute it.
#include <iostream>#include<cstdlib>#include<ctime>using namespace std;int main(){srand( time(0) );cout << "Random numbers sequence: \n\t";for(int i=1; i<10; i++)cout << rand() << " " ;return 0;}
Run the above program multiple times and see the output. Every time, it will generate a random seed and random number.
That’s it, we’re done!
Summary
In this lesson, we learned the working mechanism of the random number generation inside the rand()
function and implemented it in one possible way. Moreover, we learned why the rand()
function always generates the same sequence, unless given a random seed by using the srand(int)
function. We also learned the functionality of the time(0)
function and used time(0)
as the seed in our program to yield random sequences.