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 }