C++ std::distribution | Probability distributions in the random header
이 글의 핵심
Practical guide to std::uniform_int_distribution, normal_distribution, and related types: when to pick each, how they pair with engines, and common pitfalls.
What is a distribution?
Probability distributions (C++11)
#include <random>
std::mt19937 gen{std::random_device{}()};
// Uniform distribution
std::uniform_int_distribution<> uniform{1, 6};
int dice = uniform(gen);
// Normal distribution
std::normal_distribution<> normal{0.0, 1.0};
double value = normal(gen);
Uniform distributions
#include <random>
std::mt19937 gen{std::random_device{}()};
// Integers
std::uniform_int_distribution<> intDist{0, 99};
int randomInt = intDist(gen);
// Reals
std::uniform_real_distribution<> realDist{0.0, 1.0};
double randomDouble = realDist(gen);
Practical examples
Example 1: Normal distribution histogram
#include <random>
#include <map>
int main() {
std::mt19937 gen{std::random_device{}()};
std::normal_distribution<> dist{100.0, 15.0}; // mean 100, stddev 15
std::map<int, int> histogram;
for (int i = 0; i < 10000; ++i) {
double value = dist(gen);
++histogram[static_cast<int>(std::round(value / 10) * 10)];
}
for (const auto& [value, count] : histogram) {
std::cout << value << ": " << std::string(count / 100, '*') << std::endl;
}
}
Example 2: Bernoulli distribution
#include <random>
int main() {
std::mt19937 gen{std::random_device{}()};
std::bernoulli_distribution dist{0.7}; // 70% probability
int success = 0;
for (int i = 0; i < 100; ++i) {
if (dist(gen)) {
++success;
}
}
std::cout << "Successes: " << success << "/100" << std::endl;
}
Example 3: Binomial distribution
#include <random>
int main() {
std::mt19937 gen{std::random_device{}()};
std::binomial_distribution<> dist{10, 0.5}; // 10 trials, 50% each
for (int i = 0; i < 10; ++i) {
int successes = dist(gen);
std::cout << "Success count: " << successes << std::endl;
}
}
Example 4: Weighted discrete choice
#include <random>
#include <vector>
#include <string>
#include <map>
int main() {
std::vector<std::string> items = {"common", "uncommon", "rare", "epic", "legendary"};
std::vector<double> weights = {0.5, 0.3, 0.15, 0.04, 0.01};
std::mt19937 gen{std::random_device{}()};
std::discrete_distribution<> dist{weights.begin(), weights.end()};
std::map<std::string, int> drops;
for (int i = 0; i < 1000; ++i) {
int index = dist(gen);
++drops[items[index]];
}
for (const auto& [item, count] : drops) {
std::cout << item << ": " << count << std::endl;
}
}
Distribution families
// Uniform
std::uniform_int_distribution<> // integers
std::uniform_real_distribution<> // reals
// Bernoulli family
std::bernoulli_distribution // bool
std::binomial_distribution<> // binomial
std::geometric_distribution<> // geometric
// Normal family
std::normal_distribution<> // Gaussian
std::lognormal_distribution<> // log-normal
// Poisson family
std::poisson_distribution<> // Poisson
std::exponential_distribution<> // exponential
// Sampling
std::discrete_distribution<> // weighted index
std::piecewise_constant_distribution<>
Common pitfalls
Pitfall 1: Range semantics
// uniform_int_distribution: [a, b] inclusive
std::uniform_int_distribution<> intDist{0, 9}; // 0..9
// uniform_real_distribution: [a, b) half-open
std::uniform_real_distribution<> realDist{0.0, 1.0}; // 0.0 <= x < 1.0
Pitfall 2: Parameters
// Normal: mean, standard deviation
std::normal_distribution<> dist{100.0, 15.0};
// Invalid (negative stddev) — do not do this
// std::normal_distribution<> dist{100.0, -15.0};
auto params = dist.param();
std::cout << "mean: " << params.mean() << std::endl;
std::cout << "stddev: " << params.stddev() << std::endl;
Pitfall 3: Reset
std::mt19937 gen{std::random_device{}()};
std::uniform_int_distribution<> dist{0, 99};
dist.reset();
dist = std::uniform_int_distribution<>{100, 199};
Pitfall 4: Performance
std::mt19937 gen{std::random_device{}()};
// Recreating distribution each iteration
for (int i = 0; i < 1000; ++i) {
std::uniform_int_distribution<> dist{0, 99};
int r = dist(gen);
}
// Prefer reuse
std::uniform_int_distribution<> dist{0, 99};
for (int i = 0; i < 1000; ++i) {
int r = dist(gen);
}
Usage patterns
// 1. Uniform integers
std::uniform_int_distribution<> uniform{0, 99};
// 2. Normal
std::normal_distribution<> normal{0.0, 1.0};
// 3. Weighted choice
std::discrete_distribution<> discrete{weights.begin(), weights.end()};
// 4. Boolean trials
std::bernoulli_distribution bernoulli{0.5};
FAQ
Q1: What is a distribution here?
A: A mapping from a uniform engine output to a chosen probability law (C++11 random).
Q2: Which kinds exist?
A: Uniform, normal, Bernoulli, Poisson, exponential, discrete, and more—see the standard.
Q3: Parameters?
A: They depend on the distribution (mean/stddev for normal, bounds for uniform, etc.).
Q4: Reuse the object?
A: Yes—recreating it in a hot loop is wasteful.
Q5: Integer vs real ranges?
A: Integers are typically inclusive on both ends; reals are often [a, b).
Q6: Learning resources?
A: C++ Primer, Effective Modern C++, cppreference.com.
Related posts (internal)
- C++ Random | random numbers with <random>
- C++ random guide | rand() vs modern <random>
- C++ random_device | hardware entropy for seeding
Practical tips
Tips you can apply in real projects.
Debugging
- Fix compiler warnings first.
- Reproduce issues with a small test case.
Performance
- Do not optimize without profiling.
- Define measurable targets first.
Code review
- Check common review feedback early.
- Follow team conventions.
Production checklist
Before coding
- Is this the right tool for the problem?
- Can teammates maintain this?
- Does it meet performance needs?
While coding
- Are warnings cleared?
- Edge cases handled?
- Error handling appropriate?
At review
- Is intent clear?
- Tests sufficient?
- Documented where needed?
Use this checklist to reduce mistakes and improve quality.
Keywords (search)
C++, distribution, random, probability, C++11
Related posts
- C++ Random |
- C++ random_device |
- C++ random guide |
- C++ async & launch |
- C++ Atomic Operations |