RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
hypergeometric.hpp
Go to the documentation of this file.
1#pragma once
2
7
8namespace randlib
9{
10
17template <typename IntType>
18class HyperGeometricRand final : public DiscreteDistribution<IntType>
19{
20 IntType variate(detail::EngineRef g) const override { return (*this)(g); }
21
22 IntType N = 1;
23 IntType K = 1;
24 IntType n = 1;
25 double pmfCoef = 1;
26
27public:
28 HyperGeometricRand(IntType totalSize = 1, IntType drawsNum = 1, IntType successesNum = 1);
29 [[nodiscard]] std::string Name() const override;
30 [[nodiscard]] Support SupportType() const override { return Support::Finite; }
31 [[nodiscard]] IntType MinValue() const override { return std::max(static_cast<IntType>(0), n - N + K); }
32 [[nodiscard]] IntType MaxValue() const override { return std::min(n, K); }
33
34 void SetParameters(IntType totalSize, IntType drawsNum, IntType successesNum);
35 [[nodiscard]] inline IntType GetTotalSize() { return N; }
36 [[nodiscard]] inline IntType GetDrawsNum() { return n; }
37 [[nodiscard]] inline IntType GetSuccessesNum() { return K; }
38
39 [[nodiscard]] double P(const IntType &k) const override;
40 [[nodiscard]] double logP(const IntType &k) const override;
41 [[nodiscard]] double F(const IntType &k) const override;
42 template <std::uniform_random_bit_generator G>
43 [[nodiscard]] IntType operator()(G &g) const;
44
45private:
47 static constexpr IntType SMALL_SAMPLE = 10;
48 template <std::uniform_random_bit_generator G>
49 IntType variateByUrn(IntType draws, IntType successes, G &g) const;
50 template <std::uniform_random_bit_generator G>
51 IntType variateRatioOfUniforms(IntType draws, IntType successes, G &g) const;
52
53public:
54 [[nodiscard]] long double Mean() const override;
55 [[nodiscard]] long double Variance() const override;
56 [[nodiscard]] IntType Mode() const override;
57 [[nodiscard]] long double Skewness() const override;
58 [[nodiscard]] long double ExcessKurtosis() const override;
59};
60
61} // namespace randlib
62
65
66namespace randlib
67{
68
69template <typename IntType>
70HyperGeometricRand<IntType>::HyperGeometricRand(IntType totalSize, IntType drawsNum, IntType successesNum)
71{
72 SetParameters(totalSize, drawsNum, successesNum);
73}
74
75template <typename IntType>
77{
78 return "Hypergeometric(" + this->toStringWithPrecision(N) + ", " + this->toStringWithPrecision(n) + ", " +
79 this->toStringWithPrecision(K) + ")";
80}
81
82template <typename IntType>
83void HyperGeometricRand<IntType>::SetParameters(IntType totalSize, IntType drawsNum, IntType successesNum)
84{
85 if (!(totalSize > 0 && drawsNum > 0 && successesNum > 0))
86 throw std::invalid_argument("HyperGeometric distribution: all parameters should be positive");
87 if (!(drawsNum <= totalSize))
88 throw std::invalid_argument("HyperGeometric distribution: total size should be greater than draws number");
89 if (!(successesNum <= totalSize))
90 throw std::invalid_argument("HyperGeometric distribution: total size should be greater than successes number");
91
92 N = totalSize;
93 n = drawsNum;
94 K = successesNum;
95
96 pmfCoef = math::lfact(K);
97 pmfCoef += math::lfact(N - K);
98 pmfCoef += math::lfact(N - n);
99 pmfCoef += math::lfact(n);
100 pmfCoef -= math::lfact(N);
101}
102
103template <typename IntType>
104double HyperGeometricRand<IntType>::P(const IntType &k) const
105{
106 return (k < MinValue() || k > MaxValue()) ? 0.0 : std::exp(logP(k));
107}
108
109template <typename IntType>
110double HyperGeometricRand<IntType>::logP(const IntType &k) const
111{
112 if (k < MinValue() || k > MaxValue())
113 return -INFINITY;
114 double y = math::lfact(k);
115 y += math::lfact(K - k);
116 y += math::lfact(n - k);
117 y += math::lfact(N - K - n + k);
118 return pmfCoef - y;
119}
120
121template <typename IntType>
122double HyperGeometricRand<IntType>::F(const IntType &k) const
123{
124 IntType minVal = MinValue();
125 if (k < minVal)
126 return 0.0;
127 IntType maxVal = MaxValue();
128 if (k >= maxVal)
129 return 1.0;
130 if (k - minVal <= maxVal - k) {
132 double sum = 0;
133 for (IntType i = minVal; i <= k; ++i)
134 sum += P(i);
135 return sum;
136 }
138 double sum = 1.0;
139 for (IntType i = k + 1; i <= maxVal; ++i)
140 sum -= P(i);
141 return sum;
142}
143
144template <typename IntType>
145template <std::uniform_random_bit_generator G>
147{
150 const bool complementDraws = 2 * n > N, complementSuccesses = 2 * K > N;
151 const IntType draws = complementDraws ? N - n : n;
152 const IntType successes = complementSuccesses ? N - K : K;
153 const IntType reduced =
154 (draws < SMALL_SAMPLE) ? variateByUrn(draws, successes, g) : variateRatioOfUniforms(draws, successes, g);
155 IntType result = complementSuccesses ? draws - reduced : reduced;
156 if (complementDraws)
157 result = K - result;
158 return result;
159}
160
161template <typename IntType>
162template <std::uniform_random_bit_generator G>
163IntType HyperGeometricRand<IntType>::variateByUrn(IntType draws, IntType successes, G &g) const
164{
166 IntType sum = 0;
167 for (IntType i = 0; i < draws && sum < successes; ++i) {
168 const double p = static_cast<double>(successes - sum) / (N - i);
169 if (BernoulliRand::Variate(p, g))
170 ++sum;
171 }
172 return sum;
173}
174
175template <typename IntType>
176template <std::uniform_random_bit_generator G>
177IntType HyperGeometricRand<IntType>::variateRatioOfUniforms(IntType draws, IntType successes, G &g) const
178{
181 static constexpr double D1 = 1.7155277699214135, D2 = 0.8989161620588988;
182 const IntType failures = N - successes;
183 const double p = static_cast<double>(successes) / N, q = static_cast<double>(failures) / N;
184 const double a = draws * p + 0.5;
185 const double c = std::sqrt(static_cast<double>(N - draws) * draws * p * q / (N - 1) + 0.5);
186 const double h = D1 * c + D2;
187 const IntType mode = static_cast<IntType>(std::floor((draws + 1.0) * (successes + 1.0) / (N + 2.0)));
189 auto logRatioToPeak = [draws, successes, failures, mode](IntType k) {
190 auto ratio = [](IntType x, IntType y) {
191 return math::logFactorialRatio(static_cast<size_t>(x), static_cast<size_t>(y));
192 };
193 return ratio(mode, k) + ratio(successes - mode, successes - k) + ratio(draws - mode, draws - k) +
194 ratio(failures - draws + mode, failures - draws + k);
195 };
196 const double end = static_cast<double>(std::min(draws, successes)) + 1;
197 for (size_t iter = 0; iter < 1000 * ProbabilityDistribution<IntType>::MAX_ITER_REJECTION; ++iter) {
198 const double u = detail::uniform01<double>(g), v = detail::uniform01<double>(g);
199 const double x = a + h * (v - 0.5) / u;
200 if (x < 0 || x >= end)
201 continue;
202 const IntType k = static_cast<IntType>(std::floor(x));
203 const double t = logRatioToPeak(k);
204 if (u * (4 - u) - 3 <= t)
205 return k;
206 if (u * (u - t) >= 1)
207 continue;
208 if (2 * std::log(u) <= t)
209 return k;
210 }
211 throw std::runtime_error("HyperGeometric distribution: sampling failed");
212}
213
214template <typename IntType>
216{
217 return static_cast<long double>(n) * K / N;
218}
219
220template <typename IntType>
222{
223 if (n == N || K == N)
224 return 0.0L;
225 long double numerator = n;
226 numerator *= K;
227 numerator *= N - K;
228 numerator *= N - n;
229 long double denominator = N;
230 denominator *= N;
231 denominator *= N - 1;
232 return numerator / denominator;
233}
234
235template <typename IntType>
237{
238 long double mode = (static_cast<long double>(n) + 1.0L) * (static_cast<long double>(K) + 1.0L);
239 return static_cast<IntType>(std::floor(mode / (static_cast<long double>(N) + 2.0L)));
240}
241
242template <typename IntType>
244{
245 if (n == N || K == N)
246 return NAN;
247 if (N == 2)
248 return 0.0L;
249 long double population = N, draws = n, successes = K;
250 long double skewness = population - 1.0L;
251 skewness /= draws;
252 skewness /= successes;
253 skewness /= population - successes;
254 skewness /= population - draws;
255 skewness = std::sqrt(skewness);
256 skewness *= population - 2.0L * successes;
257 skewness *= population - 2.0L * draws;
258 return skewness / (population - 2.0L);
259}
260
261template <typename IntType>
263{
264 long double variance = Variance();
265 if (variance == 0.0L)
266 return NAN;
267 if (N <= 3) {
268 long double mean = Mean(), fourthMoment = 0.0L;
269 for (IntType k = MinValue(); k <= MaxValue(); ++k) {
270 long double centered = k - mean;
271 fourthMoment += centered * centered * centered * centered * P(k);
272 }
273 return fourthMoment / (variance * variance) - 3.0L;
274 }
275
276 long double population = N, draws = n, successes = K;
277 long double numerator = population * (population + 1.0L);
278 long double a1 = successes * (population - successes);
279 long double a2 = draws * (population - draws);
280 numerator -= 6 * (a1 + a2);
281 numerator *= population * population * (population - 1.0L);
282 long double aux = draws * successes * (population - successes) * (population - draws);
283 numerator += 6.0L * aux * (5.0L * population - 6.0L);
284 long double denominator = aux * (population - 2.0L) * (population - 3.0L);
285 return numerator / denominator;
286}
287
288} // namespace randlib
The DiscreteDistribution class Abstract class for all discrete distributions.
Definition discrete.hpp:21
The HyperGeometricRand class Hypergeometric distribution.
Definition hypergeometric.hpp:19
long double Mean() const override
Definition hypergeometric.hpp:215
IntType MaxValue() const override
Definition hypergeometric.hpp:32
HyperGeometricRand(IntType totalSize=1, IntType drawsNum=1, IntType successesNum=1)
Definition hypergeometric.hpp:70
long double Skewness() const override
Definition hypergeometric.hpp:243
double F(const IntType &k) const override
Definition hypergeometric.hpp:122
IntType Mode() const override
Mode is the value, which has the largest probability to happen, or, in the case of continuous distrib...
Definition hypergeometric.hpp:236
long double Variance() const override
Definition hypergeometric.hpp:221
double P(const IntType &k) const override
Definition hypergeometric.hpp:104
long double ExcessKurtosis() const override
Definition hypergeometric.hpp:262
IntType GetTotalSize()
Definition hypergeometric.hpp:35
std::string Name() const override
Definition hypergeometric.hpp:76
IntType MinValue() const override
Definition hypergeometric.hpp:31
Support SupportType() const override
Definition hypergeometric.hpp:30
double logP(const IntType &k) const override
Definition hypergeometric.hpp:110
void SetParameters(IntType totalSize, IntType drawsNum, IntType successesNum)
Definition hypergeometric.hpp:83
IntType GetSuccessesNum()
Definition hypergeometric.hpp:37
IntType operator()(G &g) const
Definition hypergeometric.hpp:146
IntType GetDrawsNum()
Definition hypergeometric.hpp:36
T Variate() const
Uses the engine of this object, so it is not thread-safe for one object; use operator() with one engi...
Definition distribution.hpp:133
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
Real lfact(size_t n)
Calculate log(n!) using table values for small n <= 255 and built-in lgamma function for large n > 25...
Definition math/gamma.hpp:591
Real logFactorialRatio(size_t x, size_t y)
Definition math/gamma.hpp:607
Definition bivariate_normal.hpp:10
Support
Shape of the support of a univariate distribution.
Definition univariate.hpp:15