RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
trinomial.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4
10
11namespace randlib
12{
13
23template <typename IntType>
24class TrinomialRand final : public DiscreteBivariateDistribution<BinomialRand<IntType>, BinomialRand<IntType>, IntType>
25{
26 Pair<IntType> variate(detail::EngineRef g) const override { return (*this)(g); }
27
28 int n = 1;
29 double log1mProb = -randlib::consts::ln3;
30 double p1_1mp2 = 0.5;
31 double p2_1mp1 = 0.5;
32public:
33 TrinomialRand(int number, double probability1, double probability2);
34 [[nodiscard]] std::string Name() const override;
35
36 void SetParameters(int number, double probability1, double probability2);
37 [[nodiscard]] inline int GetNumber() const noexcept { return n; }
38 [[nodiscard]] inline double GetFirstProbability() const { return this->X.GetProbability(); }
39 [[nodiscard]] inline double GetSecondProbability() const { return this->Y.GetProbability(); }
40
41 [[nodiscard]] double P(const Pair<IntType> &point) const override;
42 [[nodiscard]] double logP(const Pair<IntType> &point) const override;
43 [[nodiscard]] double F(const Pair<IntType> &point) const override;
44 template <std::uniform_random_bit_generator G>
45 [[nodiscard]] Pair<IntType> operator()(G &g) const;
46
47 long double Correlation() const override;
48
49 [[nodiscard]] Pair<IntType> Mode() const override;
50};
51
52} // namespace randlib
53
54namespace randlib
55{
56
57template <typename IntType>
58TrinomialRand<IntType>::TrinomialRand(int number, double probability1, double probability2)
59{
60 SetParameters(number, probability1, probability2);
61}
62
63template <typename IntType>
65{
66 return "Trinomial(" + this->toStringWithPrecision(GetNumber()) + ", " +
67 this->toStringWithPrecision(GetFirstProbability()) + ", " +
68 this->toStringWithPrecision(GetSecondProbability()) + ")";
69}
70
71template <typename IntType>
72void TrinomialRand<IntType>::SetParameters(int number, double probability1, double probability2)
73{
74 if (!(probability1 >= 0.0 && probability2 >= 0.0 && probability1 + probability2 <= 1.0))
75 throw std::invalid_argument("Probabilities of Trinomial distribution should be positive and their sum should "
76 "be not greater than 1, but they're equal to " +
77 std::to_string(probability1) + " and " + std::to_string(probability2));
78 if (!(number > 0))
79 throw std::invalid_argument("Number of Trinomial distribution should be positive, but it's equal to " +
80 std::to_string(number));
81
82 double p1 = probability1, p2 = probability2;
83 n = number;
84 this->X.SetParameters(n, p1);
85 this->Y.SetParameters(n, p2);
86
87 log1mProb = std::log1pl(-p1 - p2);
88 p1_1mp2 = (p2 == 1.0) ? 0.0 : p1 / (1.0 - p2);
89 p2_1mp1 = (p1 == 1.0) ? 0.0 : p2 / (1.0 - p1);
90}
91
92template <typename IntType>
93double TrinomialRand<IntType>::P(const Pair<IntType> &point) const
94{
95 IntType x = point.first, y = point.second;
96 return (x < 0 || x > n || y < 0 || y > n) ? 0.0 : std::exp(logP(point));
97}
98
99template <typename IntType>
101{
102 IntType x = point.first, y = point.second;
103 if (x < 0 || y < 0 || x > n || y > n - x)
104 return -INFINITY;
105 IntType nmxmy = n - x - y;
106 double res = this->X.GetLogFactorialN();
107 res -= math::lfact(x);
108 res -= math::lfact(y);
109 res -= math::lfact(nmxmy);
110 if (x > 0)
111 res += x * this->X.GetLogProbability();
112 if (y > 0)
113 res += y * this->Y.GetLogProbability();
114 if (nmxmy > 0)
115 res += nmxmy * log1mProb;
116 return res;
117}
118
119template <typename IntType>
120double TrinomialRand<IntType>::F(const Pair<IntType> &point) const
121{
122 int x = point.first, y = point.second;
123 if (x < 0 || y < 0)
124 return 0.0;
125 double p1 = this->X.GetProbability(), p2 = this->Y.GetProbability();
126 if (p1 == 1.0)
127 return (x >= n) ? 1.0 : 0.0;
128 if (p2 == 1.0)
129 return (y >= n) ? 1.0 : 0.0;
130 if (x >= n)
131 return this->Y.F(y);
132 if (y >= n)
133 return this->X.F(x);
134 double sum = 0.0;
135 if (x > y) {
136 double ratio = (1 - p2 - p1) / (1 - p2);
137 for (int i = 0; i <= y; ++i) {
138 int nmimx = n - i - x;
139 double prob1 = (nmimx > 0) ? math::ibeta(ratio, nmimx, x + 1) : 1.0;
140 double prob2 = this->Y.P(i);
141 sum += prob1 * prob2;
142 }
143 return sum;
144 }
145 double ratio = (1 - p1 - p2) / (1 - p1);
146 for (int i = 0; i <= x; ++i) {
147 IntType nmimy = n - i - y;
148 double prob1 = (nmimy > 0) ? math::ibeta(ratio, nmimy, y + 1) : 1.0;
149 double prob2 = this->X.P(i);
150 sum += prob1 * prob2;
151 }
152 return sum;
153}
154
155template <typename IntType>
156template <std::uniform_random_bit_generator G>
158{
159 if (this->X.GetProbability() == 1.0)
160 return IntPair(n, 0);
161 if (this->Y.GetProbability() == 1.0)
162 return IntPair(0, n);
163 if (this->X.GetProbability() > this->Y.GetProbability()) {
164 IntType x = this->X(g);
165 IntType y = BinomialDistribution<IntType>::Variate(n - x, p2_1mp1, g);
166 return IntPair(x, y);
167 }
168 IntType y = this->Y(g);
169 IntType x = BinomialDistribution<IntType>::Variate(n - y, p1_1mp2, g);
170 return IntPair(x, y);
171}
172
173template <typename IntType>
175{
176 long double p1 = this->X.GetProbability(), p2 = this->Y.GetProbability();
177 if (!(p1 > 0.0L && p1 < 1.0L && p2 > 0.0L && p2 < 1.0L))
178 return NAN;
179 return -std::sqrt(p1 * p2 / ((1.0L - p1) * (1.0L - p2)));
180}
181
182template <typename IntType>
184{
185 IntType modeX = static_cast<IntType>(std::floor(n * this->X.GetProbability()));
186 IntType modeY = static_cast<IntType>(std::floor(n * this->Y.GetProbability()));
187 modeY = std::min(modeY, static_cast<IntType>(n - modeX));
188 Pair<IntType> mode(modeX, modeY);
189 double modeValue = logP(mode);
190
191 while (true) {
192 Pair<IntType> best = mode;
193 double bestValue = modeValue;
194 auto consider = [this, &best, &bestValue](IntType x, IntType y) {
195 double value = logP(std::make_pair(x, y));
196 if (value > bestValue) {
197 best = std::make_pair(x, y);
198 bestValue = value;
199 }
200 };
201
202 if (mode.first + mode.second < n) {
203 consider(mode.first + 1, mode.second);
204 consider(mode.first, mode.second + 1);
205 }
206 if (mode.first > 0) {
207 consider(mode.first - 1, mode.second);
208 consider(mode.first - 1, mode.second + 1);
209 }
210 if (mode.second > 0) {
211 consider(mode.first, mode.second - 1);
212 consider(mode.first + 1, mode.second - 1);
213 }
214 if (best == mode)
215 return mode;
216 mode = best;
217 modeValue = bestValue;
218 }
219}
220
221} // namespace randlib
T2 Y
2nd marginal distributions
Definition bivariate.hpp:20
T1 X
1st marginal distributions
Definition bivariate.hpp:19
The DiscreteBivariateDistribution class Abstract class for all bivariate probability distributions ...
Definition discrete_bivariate.hpp:18
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 TrinomialRand class Trinomial distribution.
Definition trinomial.hpp:25
std::string Name() const override
Definition trinomial.hpp:64
double logP(const Pair< IntType > &point) const override
Definition trinomial.hpp:100
double GetFirstProbability() const
Definition trinomial.hpp:38
Pair< IntType > operator()(G &g) const
Definition trinomial.hpp:157
TrinomialRand(int number, double probability1, double probability2)
Definition trinomial.hpp:58
Pair< IntType > Mode() const override
Definition trinomial.hpp:183
void SetParameters(int number, double probability1, double probability2)
Definition trinomial.hpp:72
int GetNumber() const noexcept
Definition trinomial.hpp:37
double GetSecondProbability() const
Definition trinomial.hpp:39
double P(const Pair< IntType > &point) const override
Definition trinomial.hpp:93
long double Correlation() const override
Definition trinomial.hpp:174
double F(const Pair< IntType > &point) const override
Definition trinomial.hpp:120
The EngineRef class Non-owning, type-erased reference to any uniform random bit generator.
Definition engine/uniform.hpp:116
constexpr long double ln3
Definition constants.hpp:19
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 ibeta(Real x, std::type_identity_t< Real > a, std::type_identity_t< Real > b, std::type_identity_t< Real > logBetaFun, std::type_identity_t< Real > logX, std::type_identity_t< Real > log1mX)
Fast calculation of regularized beta function, using precalculated values.
Definition math/beta.hpp:250
Definition bivariate_normal.hpp:10
std::pair< T, T > Pair
Definition fwd.hpp:33
Pair< int > IntPair
Definition fwd.hpp:38