1 /++
2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution).
3
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5
6 Authors: John Michael Hall
7
8 Copyright: 2022-3 Mir Stat Authors.
9
10 +/
11
12 module mir.stat.distribution.uniform_discrete;
13
14 import mir.internal.utility: isFloatingPoint;
15
16 /++
17 Computes the discrete uniform probability mass function (PMF).
18
19 Params:
20 x = value to evaluate PMF
21 lower = lower bound
22 upper = upper bound
23
24 See_also:
25 $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
26 +/
27 @safe pure nothrow @nogc
28 double uniformDiscretePMF(const size_t x, const size_t lower = 0, const size_t upper = 1)
29 in (x >= lower, "x must be greater than or equal to lower bound")
30 in (x <= upper, "x must be less than or equal to upper bound")
31 in (lower <= upper, "lower must be less than or equal to upper")
32 {
33 return 1.0 / (upper - lower + 1);
34 }
35
36 ///
37 version(mir_stat_test)
38 @safe pure nothrow @nogc
39 unittest {
40 import mir.test: shouldApprox;
41
42 1.uniformDiscretePMF.shouldApprox == 0.5;
43 2.uniformDiscretePMF(1, 3).shouldApprox == 1.0 / 3;
44 }
45
46 /++
47 Computes the discrete uniform cumulative distribution function (CDF).
48
49 Params:
50 x = value to evaluate CDF
51 lower = lower bound
52 upper = upper bound
53
54 See_also:
55 $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
56 +/
57 @safe pure nothrow @nogc
58 double uniformDiscreteCDF(const size_t x, const size_t lower = 0, const size_t upper = 1)
59 in (x >= lower, "x must be greater than or equal to lower bound")
60 in (x <= upper, "x must be less than or equal to upper bound")
61 in (lower <= upper, "lower must be less than or equal to upper")
62 {
63 return (cast(double) x - lower + 1) / (upper - lower + 1);
64 }
65
66 ///
67 version(mir_stat_test)
68 @safe pure nothrow @nogc
69 unittest {
70 import mir.test: shouldApprox;
71
72 0.uniformDiscreteCDF.shouldApprox == 0.5;
73 1.uniformDiscreteCDF.shouldApprox == 1.0;
74
75 1.uniformDiscreteCDF(1, 3).shouldApprox == 1.0 / 3;
76 2.uniformDiscreteCDF(1, 3).shouldApprox == 2.0 / 3;
77 3.uniformDiscreteCDF(1, 3).shouldApprox == 1.0;
78 }
79
80 /++
81 Computes the discrete uniform complementary cumulative distribution function (CCDF).
82
83 Params:
84 x = value to evaluate CCDF
85 lower = lower bound
86 upper = upper bound
87
88 See_also:
89 $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
90 +/
91 @safe pure nothrow @nogc
92 double uniformDiscreteCCDF(const size_t x, const size_t lower = 0, const size_t upper = 1)
93 in (x >= lower, "x must be greater than or equal to lower bound")
94 in (x <= upper, "x must be less than or equal to upper bound")
95 in (lower <= upper, "lower must be less than or equal to upper")
96 {
97 return (cast(double) upper - x) / (upper - lower + 1);
98 }
99
100 ///
101 version(mir_stat_test)
102 @safe pure nothrow @nogc
103 unittest {
104 import mir.test: shouldApprox;
105
106 0.uniformDiscreteCCDF.shouldApprox == 0.5;
107 1.uniformDiscreteCCDF.shouldApprox == 0.0;
108
109 1.uniformDiscreteCCDF(1, 3).shouldApprox == 2.0 / 3;
110 2.uniformDiscreteCCDF(1, 3).shouldApprox == 1.0 / 3;
111 3.uniformDiscreteCCDF(1, 3).shouldApprox == 0.0;
112 }
113
114 /++
115 Computes the discrete uniform inverse cumulative distribution function (InvCDF)
116
117 Params:
118 p = value to evaluate InvCDF
119 lower = lower bound
120 upper = upper bound
121
122 See_also:
123 $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
124 +/
125 @safe pure nothrow @nogc
126 size_t uniformDiscreteInvCDF(T)(const T p, const size_t lower = 0, const size_t upper = 1)
127 if (isFloatingPoint!T)
128 in (p >= 0, "p must be greater than or equal to 0")
129 in (p <= 1, "p must be less than or equal to 1")
130 in (lower < upper, "lower must be less than upper")
131 {
132 size_t n = upper - lower + 1;
133 if (p * n <= 1) {
134 return lower;
135 }
136 return cast(size_t) (p * n + lower - 1);
137 }
138
139 ///.
140 version(mir_stat_test)
141 @safe pure nothrow @nogc
142 unittest {
143 import mir.test: should;
144
145 0.0.uniformDiscreteInvCDF.should == 0;
146 0.5.uniformDiscreteInvCDF.should == 0;
147 1.0.uniformDiscreteInvCDF.should == 1;
148
149 0.0.uniformDiscreteInvCDF(1, 3).should == 1;
150 0.2.uniformDiscreteInvCDF(1, 3).should == 1;
151 (1.0 / 3).uniformDiscreteInvCDF(1, 3).should == 1;
152 0.5.uniformDiscreteInvCDF(1, 3).should == 1;
153 (2.0 / 3).uniformDiscreteInvCDF(1, 3).should == 2;
154 1.0.uniformDiscreteInvCDF(1, 3).should == 3;
155 }
156
157 /++
158 Computes the discrete uniform log probability distribution function (LPDF)
159
160 Params:
161 x = value to evaluate LPDF
162 lower = lower bound
163 upper = upper bound
164
165 See_also:
166 $(LINK2 https://en.wikipedia.org/wiki/Discrete_uniform_distribution, Discrete Uniform Distribution)
167 +/
168 @safe pure nothrow @nogc
169 double uniformDiscreteLPMF(const size_t x, const size_t lower = 0, const size_t upper = 1)
170 in (x >= lower, "x must be greater than or equal to lower bound")
171 in (x <= upper, "x must be less than or equal to upper bound")
172 in (lower < upper, "lower must be less than upper")
173 {
174 import mir.math.common: log;
175
176 return -log(cast(double) upper - lower + 1);
177 }
178
179 ///
180 version(mir_stat_test)
181 @safe pure nothrow @nogc
182 unittest {
183 import mir.math.common: log;
184 import mir.test: shouldApprox;
185
186 1.uniformDiscreteLPMF.shouldApprox == -log(2.0);
187 2.uniformDiscreteLPMF(1, 3).shouldApprox == -log(3.0);
188 }