Program Listing for File cmb_random.h
↰ Return to documentation for file (include/cmb_random.h)
/*
* Copyright (c) Asbjørn M. Bonvik 1994, 1995, 2025-26.
*
* The normal and exponential distributions below are based on code at
* https://github.com/cd-mcfarland/fast_prng
* Copyright (c) Chris D McFarland 2025.
* Used with permission by author.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CIMBA_CMB_RANDOM_H
#define CIMBA_CMB_RANDOM_H
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include "cmb_assert.h"
extern void cmb_random_initialize(uint64_t seed);
extern void cmb_random_terminate(void);
extern uint64_t cmb_random_hwseed(void);
extern uint64_t cmb_random_curseed(void);
extern uint64_t cmb_random_sfc64(void);
extern uint64_t cmb_random_fmix64(uint64_t seed, uint64_t nonce);
[[maybe_unused]]
static inline double cmb_random(void)
{
return ldexp((double)(cmb_random_sfc64() >> 11), -53);
}
[[maybe_unused]]
static inline double cmb_random_uniform(const double min, const double max)
{
cmb_assert_release(min < max);
const double r = min + (max - min) * cmb_random();
cmb_assert_debug((r >= min) && (r <= max));
return r;
}
extern double cmb_random_triangular(double min, double mode, double max);
extern const uint8_t cmi_random_nor_zig_max;
extern const double cmi_random_nor_zig_pdf_x[];
extern double cmi_random_nor_not_hot(int64_t i_cand_x);
[[maybe_unused]]
static inline double cmb_random_std_normal(void)
{
uint64_t bits = cmb_random_sfc64();
const int64_t i_cand_x = *(int64_t *) &bits;
const uint8_t idx = i_cand_x & 0xFF;
return (idx <= cmi_random_nor_zig_max) ?
cmi_random_nor_zig_pdf_x[idx] * (double) i_cand_x :
cmi_random_nor_not_hot(i_cand_x);
}
[[maybe_unused]]
static inline double cmb_random_normal(const double mu, const double sigma)
{
cmb_assert_release(sigma > 0.0);
return mu + sigma * cmb_random_std_normal();
}
[[maybe_unused]]
static inline double cmb_random_lognormal(const double m, const double s)
{
cmb_assert_release(s > 0.0);
const double r = exp(cmb_random_normal(m, s));
cmb_assert_debug(r >= 0.0);
return r;
}
[[maybe_unused]]
static inline double cmb_random_logistic(const double m, const double s)
{
cmb_assert_release(s > 0.0);
const double x = cmb_random();
return m + s * log(x / (1.0 - x));
}
[[maybe_unused]]
static inline double cmb_random_cauchy(const double mode, const double scale)
{
cmb_assert_release(scale > 0.0);
const double x = cmb_random_std_normal();
double y;
while ((y = cmb_random_std_normal()) == 0.0) {}
return mode + scale * x / y;
}
extern const uint8_t cmi_random_exp_zig_max;
extern const double cmi_random_exp_zig_pdf_x[];
extern double cmi_random_exp_not_hot(uint64_t u_cand_x);
[[maybe_unused]]
static inline double cmb_random_std_exponential(void)
{
const uint64_t u_cand_x = cmb_random_sfc64();
const uint8_t idx = u_cand_x & 0xff;
double r = (idx <= cmi_random_exp_zig_max) ?
cmi_random_exp_zig_pdf_x[idx] * (double) u_cand_x :
cmi_random_exp_not_hot(u_cand_x);
cmb_assert_debug(r >= 0.0);
return r;
}
[[maybe_unused]]
static inline double cmb_random_exponential(const double mean)
{
cmb_assert_release(mean > 0.0);
double r = mean * cmb_random_std_exponential();
cmb_assert_debug(r >= 0.0);
return r;
}
[[maybe_unused]]
static inline double cmb_random_erlang(const unsigned k, const double m)
{
cmb_assert_release(k > 0u);
cmb_assert_release(m > 0.0);
double x = 0.0;
for (unsigned i = 0u; i < k; i++) {
x += cmb_random_exponential(m);
}
cmb_assert_debug(x >= 0.0);
return x;
}
[[maybe_unused]]
static inline double cmb_random_hypoexponential(const unsigned n, const double *ma)
{
cmb_assert_release(n > 0);
cmb_assert_release(ma != NULL);
double x = 0.0;
for (unsigned i = 0; i < n; i++) {
cmb_assert_release(ma[i] > 0.0);
x += cmb_random_exponential(ma[i]);
}
cmb_assert_debug(x >= 0.0);
return x;
}
extern double cmb_random_hyperexponential(unsigned n,
const double *ma,
const double *pa);
extern double cmb_random_std_gamma(double shape);
[[maybe_unused]]
static inline double cmb_random_gamma(const double shape, const double scale)
{
cmb_assert_release(shape > 0.0);
cmb_assert_release(scale > 0.0);
const double r = (shape >= 1.0) ?
scale * cmb_random_std_gamma(shape) :
scale * (cmb_random_std_gamma(shape + 1.0)
* pow(cmb_random(), 1.0 / shape));
cmb_assert_debug(r >= 0.0);
return r;
}
[[maybe_unused]]
static inline double cmb_random_std_beta(const double a, const double b)
{
cmb_assert_release(a > 0.0);
cmb_assert_release(b > 0.0);
const double x = cmb_random_std_gamma(a);
const double y = cmb_random_std_gamma(b);
const double r = x / (x + y);
cmb_assert_debug((r >= 0.0) && (r <= 1.0));
return r;
}
[[maybe_unused]]
static inline double cmb_random_beta(const double a, const double b,
const double min, const double max)
{
cmb_assert_release(a > 0.0);
cmb_assert_release(b > 0.0);
cmb_assert_release(min < max);
const double x = min + (max - min) * cmb_random_std_beta(a, b);
cmb_assert_debug((x >= min) && (x <= max));
return x;
}
extern double cmb_random_PERT_mod(double min,
double mode,
double max,
double lambda);
[[maybe_unused]]
static inline double cmb_random_PERT(const double min,
const double mode,
const double max)
{
cmb_assert_release(min < mode);
cmb_assert_release(mode < max);
const double x = cmb_random_PERT_mod(min, mode, max, 4.0);
cmb_assert_debug((x >= min) && (x <= max));
return x;
}
[[maybe_unused]]
static inline double cmb_random_weibull(const double shape, const double scale)
{
cmb_assert_release(shape > 0.0);
cmb_assert_release(scale > 0.0);
const double u = cmb_random_exponential(1.0);
const double x = scale * pow(u, 1.0 / shape);
cmb_assert_debug(x >= 0.0);
return x;
}
[[maybe_unused]]
static inline double cmb_random_pareto(const double shape, const double mode)
{
cmb_assert_release(shape > 0.0);
cmb_assert_release(mode > 0.0);
const double x = mode / pow(cmb_random(), 1.0 / shape);
cmb_assert_debug(x >= mode);
return x;
}
[[maybe_unused]]
static inline double cmb_random_chisquared(const double k)
{
cmb_assert_release(k > 0.0);
const double x = cmb_random_gamma(k / 2.0, 2.0);
cmb_assert_debug(x >= 0.0);
return x;
}
[[maybe_unused]]
static inline double cmb_random_F_dist(const double a, const double b)
{
cmb_assert_release(a > 0.0);
cmb_assert_release(b > 0.0);
const double x = cmb_random_chisquared(a) / a;
double y;
while ((y = cmb_random_chisquared(b) / b) == 0.0) {}
const double r = x / y;
cmb_assert_debug(r >= 0.0);
return r;
}
[[maybe_unused]]
static inline double cmb_random_std_t_dist(const double v)
{
cmb_assert_release(v > 0.0);
const double x = cmb_random_std_normal();
double y;
while ((y = cmb_random_chisquared(v)) == 0.0) {}
const double r = x / sqrt( y / v);
return r;
}
[[maybe_unused]]
static inline double cmb_random_t_dist(const double m,
const double s,
const double v)
{
cmb_assert_release(s > 0.0);
cmb_assert_release(v > 0.0);
const double x = m + s * cmb_random_std_t_dist(v);
return x;
}
[[maybe_unused]]
static inline double cmb_random_rayleigh(const double s)
{
cmb_assert_release(s > 0.0);
const double x = cmb_random_normal(0.0, s);
const double y = cmb_random_normal(0.0, s);
const double r = sqrt(x * x + y * y);
cmb_assert_debug(r >= 0.0);
return r;
}
extern int cmb_random_flip(void);
[[maybe_unused]]
static inline unsigned cmb_random_bernoulli(const double p)
{
cmb_assert_release((p >= 0.0) && (p <= 1.0));
return (cmb_random() <= p) ? 1 : 0;
}
extern unsigned cmb_random_geometric(double p);
extern unsigned cmb_random_binomial(unsigned n, double p);
extern unsigned cmb_random_negative_binomial(unsigned m, double p);
[[maybe_unused]]
static inline unsigned cmb_random_pascal(const unsigned m, const double p)
{
return cmb_random_negative_binomial(m, p);
}
extern unsigned cmb_random_poisson(double r);
[[maybe_unused]]
static inline long cmb_random_dice(const long a, const long b)
{
cmb_assert (a < b);
const double x = (double)(b - a + 1) * cmb_random();
return (long)(floor((double)a + x));
}
extern unsigned cmb_random_loaded_dice(unsigned n, const double *pa);
struct cmb_random_alias {
unsigned n;
uint64_t *uprob;
unsigned *alias;
};
extern struct cmb_random_alias *cmb_random_alias_create(unsigned n,
const double *pa);
[[maybe_unused]]
static inline unsigned cmb_random_alias_sample(const struct cmb_random_alias *ap)
{
cmb_assert_release(ap != NULL);
const unsigned idx = (unsigned) (floor(ap->n * cmb_random()));
const bool c = (cmb_random_sfc64() >= ap->uprob[idx]);
const unsigned r = (c) ? ap->alias[idx] : idx;
cmb_assert_debug(r < ap->n);
return r;
}
extern void cmb_random_alias_destroy(struct cmb_random_alias *ap);
#endif /* CIMBA_CMB_RANDOM_H */