36 if (n <= 1)
return 0.0;
37 return lgamma(
static_cast<double>(n + 1));
51 if (k > n)
return -std::numeric_limits<double>::infinity();
52 if (k == 0 || k == n)
return 0.0;
67 if (k > n)
return 0.0;
86inline double binomial_pmf(std::uint64_t k, std::uint64_t n,
double p)
88 if (p < 0.0 || p > 1.0) {
89 throw std::invalid_argument(
"statcpp::binomial_pmf: p must be in [0, 1]");
91 if (k > n)
return 0.0;
93 if (p == 0.0)
return (k == 0) ? 1.0 : 0.0;
94 if (p == 1.0)
return (k == n) ? 1.0 : 0.0;
96 double log_pmf =
log_binomial_coef(n, k) + k * std::log(p) + (n - k) * std::log(1.0 - p);
97 return std::exp(log_pmf);
113 if (p < 0.0 || p > 1.0) {
114 throw std::invalid_argument(
"statcpp::binomial_cdf: p must be in [0, 1]");
116 if (k >= n)
return 1.0;
120 return betainc(
static_cast<double>(n - k),
static_cast<double>(k + 1), 1.0 - p);
136 if (p < 0.0 || p > 1.0) {
137 throw std::invalid_argument(
"statcpp::binomial_quantile: p must be in [0, 1]");
139 if (prob < 0.0 || prob > 1.0) {
140 throw std::invalid_argument(
"statcpp::binomial_quantile: prob must be in [0, 1]");
142 if (prob == 0.0)
return 0;
143 if (prob == 1.0)
return n;
146 std::uint64_t lo = 0;
147 std::uint64_t hi = n;
150 std::uint64_t mid = lo + (hi - lo) / 2;
171template <
typename Engine = default_random_engine>
174 if (p < 0.0 || p > 1.0) {
175 throw std::invalid_argument(
"statcpp::binomial_rand: p must be in [0, 1]");
177 std::binomial_distribution<std::uint64_t> dist(n, p);
210 throw std::invalid_argument(
"statcpp::poisson_pmf: lambda must be non-negative");
212 if (lambda == 0.0)
return (k == 0) ? 1.0 : 0.0;
214 double log_pmf = k * std::log(lambda) - lambda -
log_factorial(k);
215 return std::exp(log_pmf);
231 throw std::invalid_argument(
"statcpp::poisson_cdf: lambda must be non-negative");
233 if (lambda == 0.0)
return 1.0;
250 throw std::invalid_argument(
"statcpp::poisson_quantile: lambda must be non-negative");
252 if (p < 0.0 || p > 1.0) {
253 throw std::invalid_argument(
"statcpp::poisson_quantile: p must be in [0, 1]");
255 if (p == 0.0)
return 0;
258 if (p == 1.0)
return std::numeric_limits<std::uint64_t>::max();
259 if (lambda == 0.0)
return 0;
263 double guess = lambda + z * std::sqrt(lambda);
264 std::uint64_t k =
static_cast<std::uint64_t
>(std::max(0.0, guess));
267 constexpr std::uint64_t kMaxIter = 1000000;
268 std::uint64_t iter = 0;
271 if (++iter >= kMaxIter)
return k;
275 if (++iter >= kMaxIter)
return k;
290template <
typename Engine = default_random_engine>
294 throw std::invalid_argument(
"statcpp::poisson_rand: lambda must be non-negative");
296 std::poisson_distribution<std::uint64_t> dist(lambda);
328 if (p <= 0.0 || p > 1.0) {
329 throw std::invalid_argument(
"statcpp::geometric_pmf: p must be in (0, 1]");
331 if (p == 1.0)
return (k == 0) ? 1.0 : 0.0;
333 return std::pow(1.0 - p,
static_cast<double>(k)) * p;
348 if (p <= 0.0 || p > 1.0) {
349 throw std::invalid_argument(
"statcpp::geometric_cdf: p must be in (0, 1]");
351 if (p == 1.0)
return 1.0;
353 return 1.0 - std::pow(1.0 - p,
static_cast<double>(k + 1));
366 if (p <= 0.0 || p > 1.0) {
367 throw std::invalid_argument(
"statcpp::geometric_quantile: p must be in (0, 1]");
369 if (prob < 0.0 || prob > 1.0) {
370 throw std::invalid_argument(
"statcpp::geometric_quantile: prob must be in [0, 1]");
372 if (prob == 0.0)
return 0;
375 if (prob == 1.0)
return std::numeric_limits<std::uint64_t>::max();
376 if (p == 1.0)
return 0;
379 double k_real = std::ceil(std::log(1.0 - prob) / std::log(1.0 - p)) - 1.0;
380 return static_cast<std::uint64_t
>(std::max(0.0, k_real));
392template <
typename Engine = default_random_engine>
395 if (p <= 0.0 || p > 1.0) {
396 throw std::invalid_argument(
"statcpp::geometric_rand: p must be in (0, 1]");
398 std::geometric_distribution<std::uint64_t> dist(p);
429inline double hypergeom_pmf(std::uint64_t k, std::uint64_t N, std::uint64_t K, std::uint64_t n)
432 throw std::invalid_argument(
"statcpp::hypergeom_pmf: K must be <= N");
435 throw std::invalid_argument(
"statcpp::hypergeom_pmf: n must be <= N");
439 std::uint64_t k_min = (n > N - K) ? n - (N - K) : 0;
440 std::uint64_t k_max = std::min(n, K);
442 if (k < k_min || k > k_max)
return 0.0;
445 return std::exp(log_pmf);
460inline double hypergeom_cdf(std::uint64_t k, std::uint64_t N, std::uint64_t K, std::uint64_t n)
463 throw std::invalid_argument(
"statcpp::hypergeom_cdf: K must be <= N");
466 throw std::invalid_argument(
"statcpp::hypergeom_cdf: n must be <= N");
469 std::uint64_t k_min = (n > N - K) ? n - (N - K) : 0;
470 std::uint64_t k_max = std::min(n, K);
472 if (k >= k_max)
return 1.0;
475 for (std::uint64_t i = k_min; i <= k; ++i) {
478 return std::min(1.0,
sum);
494 throw std::invalid_argument(
"statcpp::hypergeom_quantile: K must be <= N");
497 throw std::invalid_argument(
"statcpp::hypergeom_quantile: n must be <= N");
499 if (p < 0.0 || p > 1.0) {
500 throw std::invalid_argument(
"statcpp::hypergeom_quantile: p must be in [0, 1]");
503 std::uint64_t k_min = (n > N - K) ? n - (N - K) : 0;
504 std::uint64_t k_max = std::min(n, K);
506 if (p == 0.0)
return k_min;
507 if (p == 1.0)
return k_max;
510 for (std::uint64_t k = k_min; k <= k_max; ++k) {
512 if (cum >= p)
return k;
531template <
typename Engine = default_random_engine>
532std::uint64_t
hypergeom_rand(std::uint64_t N, std::uint64_t K, std::uint64_t n, Engine& engine)
535 throw std::invalid_argument(
"statcpp::hypergeom_rand: K must be <= N");
538 throw std::invalid_argument(
"statcpp::hypergeom_rand: n must be <= N");
542 std::uint64_t successes = 0;
543 std::uint64_t population = N;
544 std::uint64_t success_states = K;
546 std::uniform_real_distribution<double> uniform(0.0, 1.0);
548 for (std::uint64_t i = 0; i < n; ++i) {
549 double p =
static_cast<double>(success_states) /
static_cast<double>(population);
550 if (uniform(engine) < p) {
568inline std::uint64_t
hypergeom_rand(std::uint64_t N, std::uint64_t K, std::uint64_t n)
606 throw std::invalid_argument(
"statcpp::nbinom_pmf: r must be positive");
608 if (p <= 0.0 || p > 1.0) {
609 throw std::invalid_argument(
"statcpp::nbinom_pmf: p must be in (0, 1]");
611 if (p == 1.0)
return (k == 0) ? 1.0 : 0.0;
614 + r * std::log(p) + k * std::log(1.0 - p);
615 return std::exp(log_pmf);
632 throw std::invalid_argument(
"statcpp::nbinom_cdf: r must be positive");
634 if (p <= 0.0 || p > 1.0) {
635 throw std::invalid_argument(
"statcpp::nbinom_cdf: p must be in (0, 1]");
637 if (p == 1.0)
return 1.0;
639 return betainc(r,
static_cast<double>(k + 1), p);
654 throw std::invalid_argument(
"statcpp::nbinom_quantile: r must be positive");
656 if (p <= 0.0 || p > 1.0) {
657 throw std::invalid_argument(
"statcpp::nbinom_quantile: p must be in (0, 1]");
659 if (prob < 0.0 || prob > 1.0) {
660 throw std::invalid_argument(
"statcpp::nbinom_quantile: prob must be in [0, 1]");
662 if (prob == 0.0)
return 0;
665 if (prob == 1.0)
return std::numeric_limits<std::uint64_t>::max();
666 if (p == 1.0)
return 0;
669 double mean_val = r * (1.0 - p) / p;
670 double var_val = r * (1.0 - p) / (p * p);
672 double guess = mean_val + z * std::sqrt(var_val);
673 std::uint64_t k =
static_cast<std::uint64_t
>(std::max(0.0, guess));
676 constexpr std::uint64_t kMaxIter = 1000000;
677 std::uint64_t iter = 0;
678 while (k > 0 &&
nbinom_cdf(k - 1, r, p) >= prob) {
680 if (++iter >= kMaxIter)
return k;
684 if (++iter >= kMaxIter)
return k;
702template <
typename Engine = default_random_engine>
706 throw std::invalid_argument(
"statcpp::nbinom_rand: r must be positive");
708 if (p <= 0.0 || p > 1.0) {
709 throw std::invalid_argument(
"statcpp::nbinom_rand: p must be in (0, 1]");
714 std::gamma_distribution<double> gamma_dist(r, (1.0 - p) / p);
715 double y = gamma_dist(engine);
716 std::poisson_distribution<std::uint64_t> poisson_dist(y);
717 return poisson_dist(engine);
748 if (p < 0.0 || p > 1.0) {
749 throw std::invalid_argument(
"statcpp::bernoulli_pmf: p must be in [0, 1]");
770 if (p < 0.0 || p > 1.0) {
771 throw std::invalid_argument(
"statcpp::bernoulli_cdf: p must be in [0, 1]");
789 if (prob < 0.0 || prob > 1.0) {
790 throw std::invalid_argument(
"statcpp::bernoulli_quantile: prob must be in [0, 1]");
792 if (p < 0.0 || p > 1.0) {
793 throw std::invalid_argument(
"statcpp::bernoulli_quantile: p must be in [0, 1]");
795 if (prob <= 1.0 - p)
return 0;
808template <
typename Engine = default_random_engine>
811 if (p < 0.0 || p > 1.0) {
812 throw std::invalid_argument(
"statcpp::bernoulli_rand: p must be in [0, 1]");
814 std::bernoulli_distribution dist(p);
815 return dist(engine) ? 1 : 0;
847 throw std::invalid_argument(
"statcpp::discrete_uniform_pmf: a must be <= b");
849 if (k < a || k > b) {
852 return 1.0 /
static_cast<double>(b - a + 1);
867 throw std::invalid_argument(
"statcpp::discrete_uniform_cdf: a must be <= b");
875 return static_cast<double>(k - a + 1) /
static_cast<double>(b - a + 1);
890 throw std::invalid_argument(
"statcpp::discrete_uniform_quantile: a must be <= b");
892 if (p < 0.0 || p > 1.0) {
893 throw std::invalid_argument(
"statcpp::discrete_uniform_quantile: p must be in [0, 1]");
895 std::int64_t
range = b - a + 1;
896 std::int64_t k = a +
static_cast<std::int64_t
>(std::ceil(p *
static_cast<double>(
range) - 1.0));
912template <
typename Engine = default_random_engine>
916 throw std::invalid_argument(
"statcpp::discrete_uniform_rand: a must be <= b");
918 std::uniform_int_distribution<std::int64_t> dist(a, b);
std::int64_t discrete_uniform_rand(std::int64_t a, std::int64_t b, Engine &engine)
Discrete uniform distribution random number generation.
double log_binomial_coef(std::uint64_t n, std::uint64_t k)
Calculate log binomial coefficient.
double betainc(double a, double b, double x)
Regularized incomplete beta function.
double range(Iterator first, Iterator last)
Range (maximum - minimum)
double geometric_pmf(std::uint64_t k, double p)
Geometric distribution probability mass function (PMF)
std::uint64_t hypergeom_quantile(double p, std::uint64_t N, std::uint64_t K, std::uint64_t n)
Hypergeometric distribution quantile function (inverse CDF)
double hypergeom_cdf(std::uint64_t k, std::uint64_t N, std::uint64_t K, std::uint64_t n)
Hypergeometric distribution cumulative distribution function (CDF)
auto sum(Iterator first, Iterator last)
Sum.
double poisson_cdf(std::uint64_t k, double lambda)
Poisson distribution cumulative distribution function (CDF)
double norm_quantile(double p)
Standard normal quantile function.
std::uint64_t bernoulli_quantile(double prob, double p)
Bernoulli distribution quantile function.
std::uint64_t bernoulli_rand(double p, Engine &engine)
Bernoulli distribution random number generation.
double binomial_pmf(std::uint64_t k, std::uint64_t n, double p)
Binomial distribution probability mass function (PMF)
std::uint64_t nbinom_quantile(double prob, double r, double p)
Negative binomial distribution quantile function (inverse CDF)
std::uint64_t poisson_quantile(double p, double lambda)
Poisson distribution quantile function.
std::uint64_t geometric_quantile(double prob, double p)
Geometric distribution quantile function (inverse CDF)
std::uint64_t binomial_quantile(double prob, std::uint64_t n, double p)
Binomial distribution quantile function.
std::uint64_t poisson_rand(double lambda, Engine &engine)
Poisson distribution random number generation.
default_random_engine & get_random_engine()
Singleton accessor for global random engine.
double hypergeom_pmf(std::uint64_t k, std::uint64_t N, std::uint64_t K, std::uint64_t n)
Hypergeometric distribution probability mass function (PMF)
std::uint64_t hypergeom_rand(std::uint64_t N, std::uint64_t K, std::uint64_t n, Engine &engine)
Hypergeometric distribution random number generation.
std::uint64_t binomial_rand(std::uint64_t n, double p, Engine &engine)
Binomial distribution random number generation.
std::uint64_t geometric_rand(double p, Engine &engine)
Geometric distribution random number generation.
double log_factorial(std::uint64_t n)
Calculate log factorial.
double nbinom_cdf(std::uint64_t k, double r, double p)
Negative binomial distribution cumulative distribution function (CDF)
double discrete_uniform_cdf(std::int64_t k, std::int64_t a, std::int64_t b)
Discrete uniform distribution cumulative distribution function (CDF)
std::uint64_t nbinom_rand(double r, double p, Engine &engine)
Negative binomial distribution random number generation.
double gammainc_upper(double a, double x)
Upper regularized incomplete gamma function.
double geometric_cdf(std::uint64_t k, double p)
Geometric distribution cumulative distribution function (CDF)
double bernoulli_cdf(std::uint64_t k, double p)
Bernoulli distribution cumulative distribution function (CDF)
double binomial_coef(std::uint64_t n, std::uint64_t k)
Calculate binomial coefficient.
std::int64_t discrete_uniform_quantile(double p, std::int64_t a, std::int64_t b)
Discrete uniform distribution quantile function.
double binomial_cdf(std::uint64_t k, std::uint64_t n, double p)
Binomial distribution cumulative distribution function (CDF)
double poisson_pmf(std::uint64_t k, double lambda)
Poisson distribution probability mass function (PMF)
double discrete_uniform_pmf(std::int64_t k, std::int64_t a, std::int64_t b)
Discrete uniform distribution probability mass function (PMF)
double bernoulli_pmf(std::uint64_t k, double p)
Bernoulli distribution probability mass function (PMF)
double nbinom_pmf(std::uint64_t k, double r, double p)
Negative binomial distribution probability mass function (PMF)
double lgamma(double x)
Log-gamma function.
Random engine wrapper and utilities.
Special mathematical functions implementation.