Program Listing for File random.cpp

Program Listing for File random.cpp#

Return to documentation for file (/github/workspace/src/random.cpp)

#include <vector>
#include <array>
#include <random>
#include <cmath>
#include <algorithm>
#include "random.h"
#include "constants.h"

// random number seed
std::random_device rd;
//std::mt19937 twister(rd());
std::mt19937 twister(1);

double random_real()
{
    std::uniform_real_distribution<> dist(0.0, 1.0);
    return dist(twister);
}

int random_discrete(int a, int b)
{
    std::uniform_int_distribution<> dist(a, b);
    return dist(twister);
}

long long int random_poisson(double lambda)
{
    long long int result;
    if (lambda < 1e-5) {
        result = 0;
    }
    else if (lambda > 30) {
        // use normal approximation
        std::normal_distribution<> dist(lambda, std::sqrt(lambda)); // distribution(mean, standard deviation)
        int x = std::round(dist(twister));
        result = std::max(0, x);
    }
    else {
        // sample poisson directly
        std::poisson_distribution<> dist(lambda); // distribution(mean)
        result = dist(twister);
    }

    return result;
}

long long int random_binomial(long long int n, double p)
{
    long long int result;
    if (n*p > 10 && n*(1 - p) > 10) {
        // use normal approximation
        std::normal_distribution<> dist(n*p, std::sqrt(n*p*(1 - p))); // distribution(mean, standard deviation)
        long long int x = std::round(dist(twister));
        if (x<0) x=0;
        if (x>n) x=n;
        result = x;
    }
    else if ((n > 20 && p < 0.05) || (n > 100 && n*p < 10)) {
        // use Poisson approximation
        result = random_poisson(n*p);
    }
    else if ((n > 20 && p > 0.95) || (n > 100 && n*(1-p) < 10)) {
        // use Poisson approximation
        result = n - random_poisson(n*(1 - p));
    }
    else {
        // use binomial distribution directly
        std::binomial_distribution<> dist(n, p);
        result = dist(twister);
    }

    return result;
}

std::vector<long long int> random_multinomial(long long int n, const std::vector<double>& probs)
{
    int num_outcomes = probs.size();
    double sum_p = 0.0;
    for (int i = 0; i < num_outcomes; ++i) {
        sum_p += probs[i];
    }

    long long int n_used = n;
    std::vector<long long int> result(num_outcomes, 0);
    for (int i=0; i < num_outcomes; ++i) {
        if (n_used > 0) {
            result[i] = random_binomial(n_used, probs[i] / sum_p);
            sum_p -= probs[i];
            n_used -= result[i];
        }
        else {
            result[i] = 0;
        }
    }

    return result;
}

std::vector<long long int> random_multinomial(long long int n, const std::array<long long int, constants::num_gen>& probs)
{
    int num_outcomes = probs.size();
    double sum_p = 0.0;
    for (int i = 0; i < num_outcomes; ++i) {
        sum_p += probs[i];
    }

    long long int n_used = n;
    std::vector<long long int> result(num_outcomes, 0);
    for (int i=0; i < num_outcomes; ++i) {
        if (n_used > 0) {
            result[i] = random_binomial(n_used, probs[i] / sum_p);
            sum_p -= probs[i];
            n_used -= result[i];
        }
        else {
            result[i] = 0;
        }
    }

    return result;
}

std::vector<long long int> random_multinomial(long long int n, const std::array<double, constants::max_dev+1>& probs)
{
    int num_outcomes = probs.size();
    double sum_p = 0.0;
    for (int i = 0; i < num_outcomes; ++i) {
        sum_p += probs[i];
    }

    long long int n_used = n;
    std::vector<long long int> result(num_outcomes, 0);
    for (int i=0; i < num_outcomes; ++i) {
        if (n_used > 0) {
            result[i] = random_binomial(n_used, probs[i] / sum_p);
            sum_p -= probs[i];
            n_used -= result[i];
        }
        else {
            result[i] = 0;
        }
    }

    return result;
}

double random_lognormal(double des_mean, double des_var)
{
    double mean = std::log(std::pow(des_mean, 2) / std::sqrt(std::pow(des_mean, 2) + des_var));
    double var = std::log(1.0 + (des_var / std::pow(des_mean, 2)));
    std::lognormal_distribution<> dist(mean, std::sqrt(var));
    return dist(twister);
}