Component of the Week #27: bdlb_pcgrandomgenerator

Summary:
  • Provides a high-performance, high-quality random number generator using the PCG algorithm.

  • Can be used to generate multiple uncorrelated random sequences via stream selectors.

  • Can be seeded with a true random source for high-quality randomness.

The bdlb_pcgrandomgenerator component provides a mechanism class, bdlb::PcgRandomGenerator, that generates random numbers using the PCG (Permuted Congruential Generator) algorithm. PCG is a modern, high-performance random number generator that combines the speed of linear congruential generators with sophisticated permutation functions to produce high-quality random output.

A key feature of this generator is its support for stream selectors, which enable multiple generator instances to produce uncorrelated sequences even when seeded with identical initial states. This makes it particularly valuable in scenarios where you need multiple independent random streams.

Basic Usage

Creating and using a PCG random number generator is straightforward. You can initialize it with default seeds or provide your own initial state and stream selector:

#include <bdlb_pcgrandomgenerator.h>
#include <bsl_iostream.h>

using namespace BloombergLP;

int main() {
    // Create generator with default seed, and selecting the
    // default (first) stream.
    bdlb::PcgRandomGenerator rng1;

    // Create generator with custom seed, and selecting stream 5.
    bdlb::PcgRandomGenerator rng2(12345, 5);

    // Generate some random numbers
    for (int i = 0; i < 5; ++i) {
        bsl::cout << "rng1: " << rng1.generate()
                  << ", rng2: " << rng2.generate() << bsl::endl;
    }

    return 0;
}

The generate() method returns a 32-bit unsigned integer. You can transform this output to suit your needs - for example, to generate random numbers in a specific range or convert to floating-point values.

Stream Selectors for Uncorrelated Sequences

One of the most powerful features of the PCG algorithm is its ability to generate guaranteed uncorrelated sequences using stream selectors. This is particularly important in parallel computing or when you need multiple independent random streams:

#include <bdlb_pcgrandomgenerator.h>
#include <bdlb_randomdevice.h>
#include <bsl_vector.h>
#include <bsl_iostream.h>

using namespace BloombergLP;

int main() {
    // Get a truly random seed
    bsl::uint64_t trueSeed;
    bdlb::RandomDevice::getRandomBytes(
        reinterpret_cast<unsigned char*>(&trueSeed),
        sizeof(trueSeed));

    // Create multiple generators with the SAME initial state
    // but DIFFERENT stream selectors
    bdlb::PcgRandomGenerator rng1(trueSeed, 1);
    bdlb::PcgRandomGenerator rng2(trueSeed, 2);
    bdlb::PcgRandomGenerator rng3(trueSeed, 3);

    bsl::cout << "Three generators with same seed, different streams:"
              << bsl::endl;

    for (int i = 0; i < 5; ++i) {
        bsl::cout << "Step " << (i+1) << ": "
                  << rng1.generate() << " "
                  << rng2.generate() << " "
                  << rng3.generate() << bsl::endl;
    }

    // Even with identical seeds, the sequences are uncorrelated!

    return 0;
}

Generator State Management

bdlb::PcgRandomGenerator provides value semantics with equality comparison and the ability to save and restore generator state. This is useful for reproducible simulations or debugging:

#include <bdlb_pcgrandomgenerator.h>
#include <bsl_iostream.h>

using namespace BloombergLP;

int main() {
    bdlb::PcgRandomGenerator rng1(42, 1);

    // Generate some numbers
    bsl::cout << "First sequence: ";
    for (int i = 0; i < 3; ++i) {
        bsl::cout << rng1.generate() << " ";
    }
    bsl::cout << bsl::endl;

    // Save the current state
    bdlb::PcgRandomGenerator savedState = rng1;

    // Generate more numbers
    bsl::cout << "Continuing: ";
    for (int i = 0; i < 3; ++i) {
        bsl::cout << rng1.generate() << " ";
    }
    bsl::cout << bsl::endl;

    // Restore the saved state
    rng1 = savedState;

    // This will produce the same sequence as "Continuing"
    bsl::cout << "Restored state: ";
    for (int i = 0; i < 3; ++i) {
        bsl::cout << rng1.generate() << " ";
    }
    bsl::cout << bsl::endl;

    return 0;
}
For more details, see: