RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
geometric.hpp
Go to the documentation of this file.
1#pragma once
2
7
8namespace randlib
9{
10
22template <typename IntType>
23class GeometricRand final : public PascalRand<IntType>
24{
25 IntType variate(detail::EngineRef g) const override { return (*this)(g); }
26 void sample(detail::EngineRef g, std::span<IntType> outputData) const override;
27
28public:
29 explicit GeometricRand(double probability = 0.5) : PascalRand<IntType>(1, probability) {}
30 [[nodiscard]] std::string Name() const override;
31
32 void SetProbability(double probability);
33
34 [[nodiscard]] double P(const IntType &k) const override;
35 [[nodiscard]] double logP(const IntType &k) const override;
36 [[nodiscard]] double F(const IntType &k) const override;
37 [[nodiscard]] double S(const IntType &k) const override;
38 template <std::uniform_random_bit_generator G>
39 [[nodiscard]] IntType operator()(G &g) const;
40
41 template <std::uniform_random_bit_generator G>
42 [[nodiscard]] static IntType Variate(double probability, G &g);
43
44 [[nodiscard]] IntType Median() const override;
45
46 [[nodiscard]] long double Entropy() const;
47};
48
49} // namespace randlib
50
53
54namespace randlib
55{
56
57template <typename IntType>
59{
60 return "Geometric(" + this->toStringWithPrecision(this->GetProbability()) + ")";
61}
62
63template <typename IntType>
65{
66 if (!(probability > 0.0 && probability <= 1.0))
67 throw std::invalid_argument("Geometric distribution: probability parameter should be in interval (0, 1]");
68 this->SetParameters(1, probability);
69}
70
71template <typename IntType>
72double GeometricRand<IntType>::P(const IntType &k) const
73{
74 if (this->p == 1.0)
75 return (k == 0) ? 1.0 : 0.0;
76 return (k < 0) ? 0 : this->p * std::exp(k * this->log1mProb);
77}
78
79template <typename IntType>
80double GeometricRand<IntType>::logP(const IntType &k) const
81{
82 if (this->p == 1.0)
83 return (k == 0) ? 0.0 : -INFINITY;
84 return (k < 0) ? -INFINITY : this->logProb + k * this->log1mProb;
85}
86
87template <typename IntType>
88double GeometricRand<IntType>::F(const IntType &k) const
89{
90 return (k < 0) ? 0 : -std::expm1l((k + 1) * this->log1mProb);
91}
92
93template <typename IntType>
94double GeometricRand<IntType>::S(const IntType &k) const
95{
96 return (k < 0) ? 1 : std::exp((k + 1) * this->log1mProb);
97}
98
99template <typename IntType>
100template <std::uniform_random_bit_generator G>
102{
103 if (this->p == 1.0)
104 return 0;
105 typename PascalRand<IntType>::GENERATOR_ID genId = this->GetIdOfUsedGenerator();
106 if (genId == this->EXPONENTIAL)
107 return this->variateGeometricThroughExponential(g);
108 if (genId == this->TABLE)
109 return this->variateGeometricByTable(g);
111 throw std::runtime_error("Geometric distribution: sampling failed");
112}
113
114template <typename IntType>
115template <std::uniform_random_bit_generator G>
116IntType GeometricRand<IntType>::Variate(double probability, G &g)
117{
118 if (!(probability <= 1.0 && probability > 0.0))
119 throw std::invalid_argument(
120 "Geometric distribution: probability parameter should be in interval (0, 1], but it's equal to " +
121 std::to_string(probability));
122
123 if (probability == 1.0)
124 return 0;
125
127 if (probability < 0.05) {
128 double rate = -std::log1pl(-probability);
129 float X = detail::standardExponential<float>(g) / rate;
130 if (!std::isfinite(X) || X >= static_cast<long double>(std::numeric_limits<IntType>::max()))
131 return std::numeric_limits<IntType>::max();
132 return static_cast<IntType>(X);
133 }
134
135 double U = detail::uniform01<double>(g);
136 int x = 0;
137 double prod = probability, sum = prod, qprob = 1.0 - probability;
138 while (U > sum) {
139 prod *= qprob;
140 sum += prod;
141 ++x;
142 }
143 return x;
144}
145
146template <typename IntType>
147void GeometricRand<IntType>::sample(detail::EngineRef g, std::span<IntType> outputData) const
148{
149 if (this->p == 1.0) {
150 std::fill(outputData.begin(), outputData.end(), 0);
151 return;
152 }
153 typename PascalRand<IntType>::GENERATOR_ID genId = this->GetIdOfUsedGenerator();
154 if (genId == this->EXPONENTIAL) {
155 for (IntType &var : outputData)
156 var = this->variateGeometricThroughExponential(g);
157 } else if (genId == this->TABLE) {
158 for (IntType &var : outputData)
159 var = this->variateGeometricByTable(g);
160 }
161}
162
163template <typename IntType>
165{
166 if (this->p == 1.0)
167 return 0;
168 double median = -randlib::consts::ln2 / this->log1mProb;
169 double flooredMedian = std::floor(median);
170 return (math::detail::areClose(median, flooredMedian, 1e-8)) ? flooredMedian - 1 : flooredMedian;
171}
172
173template <typename IntType>
175{
176 if (this->p == 1.0)
177 return 0.0;
178 double a = -this->q * this->log1mProb;
179 double b = -this->p * this->logProb;
180 return (a + b) / this->p;
181}
182
183} // namespace randlib
The GeometricRand class Geometric distribution.
Definition geometric.hpp:24
void SetProbability(double probability)
Definition geometric.hpp:64
static IntType Variate(double probability, G &g)
Definition geometric.hpp:116
std::string Name() const override
Definition geometric.hpp:58
double S(const IntType &k) const override
Definition geometric.hpp:94
IntType operator()(G &g) const
Definition geometric.hpp:101
GeometricRand(double probability=0.5)
Definition geometric.hpp:29
double P(const IntType &k) const override
Definition geometric.hpp:72
IntType Median() const override
Definition geometric.hpp:164
double F(const IntType &k) const override
Definition geometric.hpp:88
double logP(const IntType &k) const override
Definition geometric.hpp:80
long double Entropy() const
Definition geometric.hpp:174
GENERATOR_ID
Definition negative_binomial.hpp:69
The NegativeBinomialRand class Negative binomial distribution.
Definition negative_binomial.hpp:133
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
constexpr long double ln2
Definition constants.hpp:18
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
bool areClose(RealType a, RealType b, RealType eps=1e-6)
Definition functions.hpp:38
Definition bivariate_normal.hpp:10