How to generate a random number in C++?
Categories:
Generating Random Numbers in C++: A Comprehensive Guide

Learn the correct and modern ways to generate pseudo-random numbers in C++, avoiding common pitfalls and ensuring statistical quality for various applications.
Generating random numbers is a fundamental task in many programming domains, from simulations and games to cryptography and statistical analysis. In C++, the approach to generating pseudo-random numbers has evolved significantly. This article will guide you through the modern C++ <random>
library, explaining why older methods like rand()
and srand()
are often insufficient and how to use the new facilities effectively.
The Problem with rand()
and srand()
Historically, C++ programmers relied on the C standard library functions rand()
and srand()
for random number generation. While simple to use, these functions suffer from several limitations that make them unsuitable for most modern applications:
- Poor Quality:
rand()
often produces low-quality pseudo-random numbers with short periods and predictable patterns, especially on older compilers. - Global State:
rand()
andsrand()
operate on a global state, making them difficult to use safely in multi-threaded environments or when multiple independent random sequences are needed. - Limited Range:
rand()
typically returns an integer between 0 andRAND_MAX
(which is often only 32767), requiring manual scaling and modulo operations to fit a desired range, which can introduce bias. - Non-Uniform Distribution: Simple modulo operations on
rand()
results can lead to non-uniform distributions, where some numbers are more likely to appear than others.
#include <iostream>
#include <cstdlib> // For rand() and srand()
#include <ctime> // For time()
int main() {
// Seed the random number generator (only once per program execution)
srand(time(0));
// Generate a random number between 0 and RAND_MAX
int randomNumber = rand();
std::cout << "Raw rand(): " << randomNumber << std::endl;
// Generate a random number between 1 and 100 (biased method)
int biasedNumber = (rand() % 100) + 1;
std::cout << "Biased rand() (1-100): " << biasedNumber << std::endl;
return 0;
}
Example of using rand()
and srand()
(demonstrates common pitfalls).
rand()
and srand()
for any application requiring high-quality or statistically sound random numbers. They are generally considered deprecated in modern C++ for new code.The Modern C++ <random>
Library
C++11 introduced the <random>
library, a powerful and flexible framework for generating high-quality pseudo-random numbers. It separates the concerns of random number generation into three main components:
- Random Number Engines: These are the actual generators that produce sequences of raw, uniformly distributed unsigned integer values. Examples include
std::mt19937
(Mersenne Twister) andstd::default_random_engine
. - Seed Sequences: Used to initialize random number engines with high-quality seed values, especially when multiple engines need to be initialized differently.
- Random Number Distributions: These take the raw output from an engine and transform it into numbers that follow a specific statistical distribution (e.g., uniform, normal, Bernoulli) within a desired range.
flowchart TD A[Seed Source] --> B[Seed Sequence (optional)] B --> C[Random Number Engine] C --> D[Random Number Distribution] D --> E[Desired Random Number] subgraph Components C -- "Raw Uniform Integers" --> D end style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#bbf,stroke:#333,stroke-width:2px style C fill:#ccf,stroke:#333,stroke-width:2px style D fill:#cfc,stroke:#333,stroke-width:2px style E fill:#ffc,stroke:#333,stroke-width:2px
Conceptual flow of random number generation using the C++ <random>
library.
Generating Uniform Random Integers
The most common use case is generating uniformly distributed integers within a specific range. This is achieved using a random number engine (e.g., std::mt19937
) and a std::uniform_int_distribution
.
#include <iostream>
#include <random> // For random number generation
#include <chrono> // For high-resolution clock seeding
int main() {
// 1. Create a random number engine
// Using std::mt19937 (Mersenne Twister) is a good general-purpose choice.
// Seed it with a high-resolution clock for better randomness.
std::mt19937 engine(std::chrono::high_resolution_clock::now().time_since_epoch().count());
// 2. Define a distribution
// For integers between 1 and 100 (inclusive).
std::uniform_int_distribution<int> dist(1, 100);
// 3. Generate and print random numbers
std::cout << "Random numbers (1-100):\n";
for (int i = 0; i < 5; ++i) {
std::cout << dist(engine) << " ";
}
std::cout << std::endl;
return 0;
}
Generating uniform random integers using std::mt19937
and std::uniform_int_distribution
.
time(0)
or high_resolution_clock
) doesn't change fast enough.Generating Uniform Random Floating-Point Numbers
Similar to integers, you can generate uniformly distributed floating-point numbers within a specified range using std::uniform_real_distribution
.
#include <iostream>
#include <random>
#include <chrono>
#include <iomanip> // For std::fixed and std::setprecision
int main() {
std::mt19937 engine(std::chrono::high_resolution_clock::now().time_since_epoch().count());
// For floating-point numbers between 0.0 and 1.0 (inclusive)
std::uniform_real_distribution<double> dist(0.0, 1.0);
std::cout << "Random doubles (0.0-1.0):\n";
std::cout << std::fixed << std::setprecision(5);
for (int i = 0; i < 5; ++i) {
std::cout << dist(engine) << " ";
}
std::cout << std::endl;
// For floating-point numbers between -5.0 and 5.0
std::uniform_real_distribution<double> dist2(-5.0, 5.0);
std::cout << "Random doubles (-5.0-5.0):\n";
for (int i = 0; i < 5; ++i) {
std::cout << dist2(engine) << " ";
}
std::cout << std::endl;
return 0;
}
Generating uniform random floating-point numbers.
Other Distributions and Best Practices
The <random>
library offers a variety of other distributions, such as std::normal_distribution
for Gaussian (bell curve) numbers, std::bernoulli_distribution
for true/false outcomes, and many more. When working with random numbers, consider these best practices:
- Use a single engine instance: Create one instance of your random number engine (e.g.,
std::mt19937
) and pass it by reference to functions that need to generate random numbers. Avoid creating new engines or re-seeding repeatedly. - Thread safety: If generating random numbers in multiple threads, each thread should have its own independent random number engine instance, seeded differently, to avoid contention and ensure independent sequences.
- Cryptographic randomness: For security-sensitive applications (e.g., generating keys, nonces), the
<random>
library is generally not sufficient. Usestd::random_device
directly or a dedicated cryptographic library for true hardware-based randomness.
#include <iostream>
#include <random>
#include <chrono>
// Function to generate a random integer within a range
int getRandomInt(std::mt19937& engine, int min, int max) {
std::uniform_int_distribution<int> dist(min, max);
return dist(engine);
}
int main() {
// Seed the engine once
std::mt19937 engine(std::chrono::high_resolution_clock::now().time_since_epoch().count());
std::cout << "Generating 3 random numbers between 10 and 20:\n";
for (int i = 0; i < 3; ++i) {
std::cout << getRandomInt(engine, 10, 20) << " ";
}
std::cout << std::endl;
// Example of normal distribution
std::normal_distribution<double> normal_dist(0.0, 1.0); // Mean 0, Std Dev 1
std::cout << "\nGenerating 3 numbers from a normal distribution (mean 0, std dev 1):\n";
for (int i = 0; i < 3; ++i) {
std::cout << normal_dist(engine) << " ";
}
std::cout << std::endl;
return 0;
}
Demonstrating passing an engine by reference and using std::normal_distribution
.