Pseudorandom Number Generation and Common Errors
Pseudorandom Number Generation
Pseudorandom number generation was not covered in the lecture but may appear in future assignments, studios, or the final exam. Therefore, this studio begins by introducing the topic, which you’ll explore briefly below.
Modern digital computers are designed to be deterministic, meaning they are intended to behave predictably. However, many software applications benefit from simulating randomness. For example, randomness can be used to implement game mechanics, create statistical models, or design orchestration systems that distribute requests randomly across servers.
Since computers are inherently deterministic, they cannot generate true randomness. Instead, they rely on pseudorandomness, which appears random but is produced through a deterministic process. A pseudorandom number generator (PRNG) is a software tool that generates numbers that seem random but are mathematically derived. While these numbers are predictable in theory, the underlying process is often complex enough to make reverse engineering impractical for humans. Some advanced PRNGs, such as cryptographically secure PRNGs, even draw entropy from user interactions like mouse movements, making them more unpredictable.
In C++, a PRNG is available in the standard library. To use it, include the <cstdlib>
header at the top of your program. This gives you access to the srand
and rand
functions. Note that these functions are not part of the std
namespace, so they do not require a std::
prefix.
How PRNGs Work
Most PRNGs start with a number called the random seed. This seed undergoes a series of complex mathematical transformations to generate the next number in the pseudorandom sequence. The output of this process is then used as the seed for the next iteration, creating a sequence of numbers. A good PRNG ensures that small changes in the seed result in significant and seemingly arbitrary differences in the output sequence.
By default, C++ initializes the PRNG with a seed of 0
. As a result, running the program repeatedly will produce the same sequence of pseudorandom numbers. To avoid this, you must ensure that the seed is different for each program execution.
Using the Current Time as a Seed
A common approach is to derive the seed from the current time. Specifically, the number of seconds since the Unix epoch (midnight, January 1, 1970) is often used. To retrieve this value, include the <ctime>
header and use one of the following expressions: time(nullptr)
, time(NULL)
, or time(0)
.
To set the PRNG seed, use the srand
function provided by <cstdlib>
. This function accepts an integer argument representing the seed. Typically, you should call srand
only once at the beginning of your program, usually in the main function.
Example of setting the seed:
srand(time(nullptr));
Generating Pseudorandom Numbers
Once the PRNG is seeded, you can generate pseudorandom numbers by calling the rand
function. This function returns a positive integer between 0
and a constant value RAND_MAX
. If you need numbers in a specific range, such as a die roll (1 to 6) or a probability (0.0 to 1.0), you’ll need to adjust the output of rand
using mathematical operations.
Here’s an example program that demonstrates how to generate random numbers:
#include <iostream>#include <cstdlib> // For rand() and srand()#include <ctime> // For time()
int main() { // Seed the PRNG at the start of the program srand(time(nullptr));
// Generate and print two random integers std::cout << "Value 1: " << rand() << std::endl;
// Store a random value in a variable int another_value = rand(); std::cout << "Value 2: " << another_value << std::endl;
return 0;}
Guiding Questions
Answer the following questions based on a PRNG seeded with srand(time(nullptr))
at the beginning of the main function:
- What is the value of
0 % 4
? - What is the value of
1 % 4
? - What is the value of
2 % 4
? - What is the value of
3 % 4
? - What is the value of
4 % 4
? - What is the value of
5 % 4
? - Identify the interval in which
x % 4
lies, regardless of the value ofx
. - Identify the interval in which
x % 10
lies, regardless of the value ofx
. - Construct an expression using the above information to generate a pseudorandom integer between 0 and 5, inclusive.
- Given a pseudorandom integer between 0 and 10, explain how to transform it into a pseudorandom integer between 5 and 15.
- Construct an expression that generates a pseudorandom integer between 5 and 15, inclusive.
- Construct an expression that generates a pseudorandom integer between
a
andb
(inclusive), wherea
andb
areint
variables. - Construct an expression that generates a pseudorandom
double
value between 0.0 and 1.0, inclusive. Usestatic_cast
to avoid truncation. - Assume the PRNG generates uniform random numbers between 0 and 1. Create a boolean expression that evaluates to true 54.3% of the time and false 45.7% of the time.
Program
Write a program that simulates rolling two dice. Each die should generate a pseudorandom integer between 1 and 6. Print the two values, and if the rolls are equal, display the message: “You rolled doubles!”
Common Errors
This section is dedicated to helping you identify and resolve common errors in a C++ program. Each error is accompanied by an example, an explanation, and suggestions for fixes.
Syntax Errors
Error 1: Variable Not Declared in Scope
Suppose your compiler outputs the following error message when trying to compile a program:
main.cpp: In function 'double triple_number()':main.cpp:4:16: error: 'favorite_number' was not declared in this scope4 | return favorite_number * 3; | ^~~~~~~~~~~~~~~main.cpp: In function 'int main()':main.cpp:12:47: error: too many arguments to function 'double triple_number()'12 | double triple_favorite = triple_number(favorite_number); | ^~~~~~~~~~~~~~main.cpp:3:8: note: declared here3 | double triple_number() { | ^~~~~~~~~~~~~~
Read this error carefully. Focus especially on the beginning of the error message. What does the error mean? On what line does the error directly occur? In what function does the error directly occur? You should be able to figure out the answers to these questions without looking at the associated code, but for context, suppose the code looks like this:
#include <iostream>
double triple_number() { return favorite_number * 3;}
int main() { std::cout << "Enter your favorite number, and I'll triple it for you: "; double favorite_number; std::cin >> favorite_number;
double triple_favorite = triple_number(favorite_number); std::cout << "Your number, tripled, is: " << triple_favorite << std::endl;}
Looking at the code and the error message, what changes might you make to the code to resolve the error?
Error 2: Missing Return Statement and Variable Not Declared
Suppose your compiler outputs the following error message when trying to compile a program:
main.cpp: In function 'char prompt_for_character()':main.cpp:7:1: warning: no return statement in function returning non-void [-Wreturn-type]7 | } | ^main.cpp: In function 'int main()':main.cpp:11:65: error: 'initial' was not declared in this scope11 | std::cout << "You said that your first initial is: " << initial; | ^~~~~~
Read this error carefully. Focus especially on the beginning of the error message. What does the error mean? On what line does the error directly occur? In what function does the error directly occur? You should be able to figure out the answers to these questions without looking at the associated code, but for context, suppose the code looks like this:
#include <iostream>
char prompt_for_character() { std::cout << "What is your first initial? Enter a single character: "; char initial; std::cin >> initial;}
int main() { prompt_for_character(); std::cout << "You said that your first initial is: " << initial;}
Looking at the code and the error message, what changes might you make to the code to resolve the error?
Logic Errors
Error 3: Incorrect Mathematical Expression
Suppose you want to convert the mathematical statement to a C++ function. You’ve implemented the function for it, but your compiler outputs the following error message when trying to compile your code:
main.cpp: In function 'double f(double)':main.cpp:4:16: error: unable to find numeric literal operator 'operator""x'4 | return 2x + 5; | ^
Read this error carefully. Focus especially on the beginning of the error message. What does the error mean? On what line does the error directly occur? In what function does the error directly occur? You should be able to figure out the answers to these questions without looking at the associated code, but for context, suppose the code looks like this:
#include <iostream>
double f(double x) { return 2x + 5;}...
Looking at the code and the error message, what changes might you make to the code to resolve the error?
Error 4: Incorrect Conditional Logic
You’ve written a program that recommends a board game to the user based on their age. However, you’ve noticed that there’s some sort of logic error—it always recommends “Candyland” to them, regardless of their age.
#include <iostream>
/*** Function: prompt_for_age* Description: Prompts for user’s age* Returns (int): User’s age as given through the terminal*/int prompt_for_age() { std::cout << "Enter your age: "; int age; std::cin >> age; return age;}
/*** Function: print_board_game_recommendation* Description: Prints a board game recommendation based on the user’s age* Parameters:* age (int): User’s age*/void print_board_game_recommendation(int age) { if (2 <= age <= 7) { std::cout << "Candyland" << std::endl; } else if (8 <= age <= 12) { std::cout << "Monopoly" << std::endl; } else if (age >= 13) { std::cout << "Munchkin" << std::endl; }}
int main() { // Get user’s age int age = prompt_for_age(); // Print board game recommendation print_board_game_recommendation(age);}
What’s causing the logic error, and how would you fix it?