Introduction
The primary focus of this guide pertains to the std::exponential_distribution class in C++, a valuable component of the Standard Library for generating random numbers following an exponential distribution. This specific distribution is commonly utilized when analyzing the time intervals between events in a Poisson process, such as in queuing theory, reliability engineering, and survival analysis.
In this guide, we will delve into the std::exponential_distribution class, its functionality, and practical applications in real-life scenarios.
Exponential Distribution
The exponential distribution represents a continuous random variable distribution that is suitable for representing the intervals between occurrences in a Poisson process. It is characterized by a single parameter, denoted as λ, which signifies the rate parameter.
Where,
- x= The variable x denoting time
- λ= The parameter λ indicating the rate.
Necessary Headers
If you intend to utilize std::exponential_distribution, ensure to include the <random> header. This specific header includes essential classes and functions required for generating random numbers.
#include <iostream>
#include <random>
Creating an Exponential Distribution
Creating a std::exponential_distribution instance can be achieved by utilizing either the default constructor or a parameterized constructor of the random engine.
- Default Constructor:
To begin with, the default constructor initializes the distribution with a default rate parameter of λ=1.0.
std::exponential_distribution<> distribution;
- Parameterized Constructor:
- Enables us to set up the parameter of occurrence rate λ.
std::exponential_distribution<> distribution(double lambda);
About Random Header
The <random"> header is a part of the C++ Standard Library and has been integrated into the C++11 language. It provides a range of functions for generating random numbers and performing various operations on them. Specifically, it includes a class for generating random number generators, classes for working with different distributions of random numbers, and managing the various types of random number generators.
Components of Random Header:
- Random Number Engines
Some Examples are:
- Linear congruential generators - std::minstdrand0, std . minstd_rand
- Subtractive random number generators - std::(random. number. generation) Converting it to C++, there are several available random number generators namely: ranlux24base, std::ranlux48base, std::ranlux24, std::ranlux48
- Random Number Distributions
Some Examples are:
- Uniform distribution of integer values - std::ApplammedIt gave uniformintdistribution
- Uniform distribution of floating-point values - std::uniformrealdistribution: Boolean distribution (true/false) - The three arguments allowed for Bernoulli distribution are:
- Other specialized distributions - std: It included generating an extreme value distribution based on the extreme events detected in the analysis that we conducted.
- Probabilistic generalization and non-deterministic generation
Some instances include:
- Grants access to non-deterministic random numbers, if they are accessible - std::The variable further exhibits an extreme value distribution, ensuring it never falls below the value of 1.
- Additional Tools
Some instances include:
- A series of initial seed values for random number engine initialization - std:::seed: seed
Example 1 of Random Header
Problem Description: Generating Random Numbers with Uniform Distribution
#include <iostream>
#include <random>
One common requirement in programming is the need to generate random numbers that are uniformly distributed. This means that each possible outcome has an equal probability of occurring. To achieve this, various methods and algorithms can be used. In this tutorial, we will explore some approaches to generating uniformly distributed random numbers in programming.
#include <iostream>
#include <random>
int main() {
// This is to Create a random device to seed the random number generator
std::random_device RD;
// This is to use Mersenne Twister engine for generating random numbers
std::mt19937 gen(RD());
// This is to Define a uniform integer distribution in the range [1, 100]
std::uniform_int_distribution<> dis(1, 50);
// This is to generate and print 10 random numbers
for (int n = 0; n < 5; ++n) {
std::cout << dis(gen) << ' ';
}
std::cout << std::endl;
return 0;
}
Output 1:
30 40 15 23 31
=== Code Execution Successful ===
Output 2:
2 30 10 26 30
=== Code Execution Successful ===
Explanation of the Code:
- Initialization:
std::random_device RD;
std::mt19937 gen(RD());
- Distribution Definition:
std::uniform_int_distribution<> dis(1, 50);
This method ensures an even distribution for generating whole numbers within the range of 1 to 50.
- Generating Numbers:
for (int n = 0; n < 5; ++n) {
std::cout << dis(gen) << ' ';
}
A loop is responsible for generating and displaying five random integers based on the specified distribution.
Example 2 of Random Header
Problem Description: Simulating Arrival of Network Packets Using Exponential Distribution
#include <iostream>
#include <random>
#include <vector>
#include <numeric> // For std::accumulate
#include <algorithm> // For std::max_element
struct Packet {
double arrival_time;
double size; // Size of the packet
};
int main() {
// Simulation parameters
const double arrival_rate = 0.5; // Average 2 packets per time unit (1/arrival_rate)
const double size_mean = 500.0; // Mean size of packets in bytes
const double size_stddev = 50.0; // Standard deviation of packet size
const int num_packets = 30; // Number of packets to simulate
// Random number generation
std::random_device rd;
std::mt19937 gen(rd());
std::exponential_distribution<> arrival_dist(arrival_rate);
std::normal_distribution<> size_dist(size_mean, size_stddev);
// Vector to store generated packets
std::vector<Packet> packets;
double current_time = 0.0;
for (int i = 0; i < num_packets; ++i) {
// Generate the inter-arrival time of the next packet
double interarrival_time = arrival_dist(gen);
current_time += interarrival_time;
// Generate the size of the packet
double packet_size = size_dist(gen);
if (packet_size < 0) {
packet_size = 0; // Ensure packet size is not negative
}
Packet packet = {current_time, packet_size};
packets.push_back(packet);
std::cout << "Packet " << i + 1 << " arrived at " << current_time
<< " with size " << packet_size << " bytes." << std::endl;
}
// Calculate and print statistics
double total_interarrival_time = packets.back().arrival_time;
double total_packet_size = std::accumulate(packets.begin(), packets.end(), 0.0,
[](double sum, const Packet& packet) { return sum + packet.size; });
double average_interarrival_time = total_interarrival_time / num_packets;
double average_packet_size = total_packet_size / num_packets;
double max_packet_size = std::max_element(packets.begin(), packets.end(),
[](const Packet& a, const Packet& b) { return a.size < b.size; })->size;
std::cout << "\nTotal packets: " << num_packets << std::endl;
std::cout << "Average inter-arrival time: " << average_interarrival_time << " time units" << std::endl;
std::cout << "Average packet size: " << average_packet_size << " bytes" << std::endl;
std::cout << "Maximum packet size: " << max_packet_size << " bytes" << std::endl;
return 0;
}
Output:
Packet 1 arrived at 2.647 with size 549.662 bytes.
Packet 2 arrived at 6.968 with size 392.527 bytes.
Packet 3 arrived at 8.1762 with size 446.382 bytes.
Packet 4 arrived at 12.1417 with size 422.995 bytes.
Packet 5 arrived at 12.8545 with size 406.127 bytes.
Packet 6 arrived at 13.187 with size 514.723 bytes.
Packet 7 arrived at 15.2886 with size 413.032 bytes.
Packet 8 arrived at 16.0074 with size 487.628 bytes.
Packet 9 arrived at 17.4922 with size 520.079 bytes.
Packet 10 arrived at 19.9851 with size 448.265 bytes.
Packet 11 arrived at 26.9368 with size 458.188 bytes.
Packet 12 arrived at 29.2227 with size 498.873 bytes.
Packet 13 arrived at 29.3468 with size 451.223 bytes.
Packet 14 arrived at 31.1982 with size 475.573 bytes.
Packet 15 arrived at 32.1214 with size 509.195 bytes.
Packet 16 arrived at 33.9577 with size 362.997 bytes.
Packet 17 arrived at 34.5125 with size 565.237 bytes.
Packet 18 arrived at 36.897 with size 483.421 bytes.
Packet 19 arrived at 40.8254 with size 482.831 bytes.
Packet 20 arrived at 40.9058 with size 505.229 bytes.
Packet 21 arrived at 41.9937 with size 439.408 bytes.
Packet 22 arrived at 43.3056 with size 488.416 bytes.
Packet 23 arrived at 43.9117 with size 586.987 bytes.
Packet 24 arrived at 45.6715 with size 531.251 bytes.
Packet 25 arrived at 46.9939 with size 368.973 bytes.
Packet 26 arrived at 52.3196 with size 439.729 bytes.
Packet 27 arrived at 53.1564 with size 447.493 bytes.
Packet 28 arrived at 53.3603 with size 523.402 bytes.
Packet 29 arrived at 53.479 with size 472.463 bytes.
Packet 30 arrived at 60.5505 with size 511.338 bytes.
Total packets: 30
Average inter-arrival time: 2.01835 time units
Average packet size: 473.455 bytes
Maximum packet size: 586.987 bytes
=== Code Execution Successful ===
Explanation:
- Simulation Parameters
const double arrival_rate = 0.5;
const double size_mean = 500.0;
const double size_stddev = 50.0;
const int num_packets = 30;
These constants establish the simulation parameters: arrivalrate indicates the average rate at which packets arrive, sizemean and sizestddev determine the average and variation in packet sizes, and numpackets specifies the quantity of packets to simulate.
- Random Number Generation
std::random_device rd;
std::mt19937 gen(rd());
std::exponential_distribution<> arrival_dist(arrival_rate);
std::normal_distribution<> size_dist(size_mean, size_stddev);
Here, we set up the environment for generating random numbers. It employs a randomdevice to initialize the Mersenne Twister engine, which is referred to as std::mt19937. The arrivaldist field generates intervals between arrivals, while the size_dist field determines the sizes of packets.
- Creating Packets
std::vector<Packet> packets;
double current_time = 0.0;
for (int i = 0; i < num_packets; ++i) {
double interarrival_time = arrival_dist(gen);
current_time += interarrival_time;
double packet_size = size_dist(gen);
if (packet_size < 0) {
packet_size = 0;
}
Packet packet = {current_time, packet_size};
packets.push_back(packet);
std::cout << "Packet " << i + 1 << " arrived at " << current_time
<< " with size " << packet_size << " bytes." << std::endl;
}
To generate num_packets packets, a loop is established. Each packet's inter-arrival time is calculated, added to the current time, packet size is determined ensuring it's above zero, and then the packet is stored in a vector for detailed printing.
- Computing Statistics
double total_interarrival_time = packets.back().arrival_time;
double total_packet_size = std::accumulate(packets.begin(), packets.end(), 0.0,
[](double sum, const Packet& packet) { return sum + packet.size; });
double average_interarrival_time = total_interarrival_time / num_packets;
double average_packet_size = total_packet_size / num_packets;
double max_packet_size = std::max_element(packets.begin(), packets.end(),
[](const Packet& a, const Packet& b) { return a.size < b.size; })->size;
These factors consist of the total time between arrivals, total size of packets, mean time between packet arrivals, average packet size, and the maximum packet size among the packets received using built-in library functions.
- Statistical Results
std::cout << "\nTotal packets: " << num_packets << std::endl;
std::cout << "Average inter-arrival time: " << average_interarrival_time << " time units" << std::endl;
std::cout << "Average packet size: " << average_packet_size << " bytes" << std::endl;
std::cout << "Maximum packet size: " << max_packet_size << " bytes" << std::endl;
It displays various metrics on the console, providing insights into the outcomes of simulated data packets.