1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.hipparchus.distribution.discrete;
24
25 import org.hipparchus.exception.LocalizedCoreFormats;
26 import org.hipparchus.exception.MathIllegalArgumentException;
27 import org.hipparchus.util.FastMath;
28
29
30
31
32
33
34
35 public class HypergeometricDistribution extends AbstractIntegerDistribution {
36
37 private static final long serialVersionUID = 20160320L;
38
39 private final int numberOfSuccesses;
40
41 private final int populationSize;
42
43 private final int sampleSize;
44
45 private final double numericalVariance;
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public HypergeometricDistribution(int populationSize, int numberOfSuccesses, int sampleSize)
60 throws MathIllegalArgumentException {
61 if (populationSize <= 0) {
62 throw new MathIllegalArgumentException(LocalizedCoreFormats.POPULATION_SIZE,
63 populationSize);
64 }
65 if (numberOfSuccesses < 0) {
66 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_SUCCESSES,
67 numberOfSuccesses);
68 }
69 if (sampleSize < 0) {
70 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_SAMPLES,
71 sampleSize);
72 }
73
74 if (numberOfSuccesses > populationSize) {
75 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE,
76 numberOfSuccesses, populationSize, true);
77 }
78 if (sampleSize > populationSize) {
79 throw new MathIllegalArgumentException(LocalizedCoreFormats.SAMPLE_SIZE_LARGER_THAN_POPULATION_SIZE,
80 sampleSize, populationSize, true);
81 }
82
83 this.numberOfSuccesses = numberOfSuccesses;
84 this.populationSize = populationSize;
85 this.sampleSize = sampleSize;
86 this.numericalVariance = calculateNumericalVariance();
87 }
88
89
90 @Override
91 public double cumulativeProbability(int x) {
92 double ret;
93
94 int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
95 if (x < domain[0]) {
96 ret = 0.0;
97 } else if (x >= domain[1]) {
98 ret = 1.0;
99 } else {
100 ret = innerCumulativeProbability(domain[0], x, 1);
101 }
102
103 return ret;
104 }
105
106
107
108
109
110
111
112
113
114
115 private int[] getDomain(int n, int m, int k) {
116 return new int[] { getLowerDomain(n, m, k), getUpperDomain(m, k) };
117 }
118
119
120
121
122
123
124
125
126
127
128 private int getLowerDomain(int n, int m, int k) {
129 return FastMath.max(0, m - (n - k));
130 }
131
132
133
134
135
136
137 public int getNumberOfSuccesses() {
138 return numberOfSuccesses;
139 }
140
141
142
143
144
145
146 public int getPopulationSize() {
147 return populationSize;
148 }
149
150
151
152
153
154
155 public int getSampleSize() {
156 return sampleSize;
157 }
158
159
160
161
162
163
164
165
166
167 private int getUpperDomain(int m, int k) {
168 return FastMath.min(k, m);
169 }
170
171
172 @Override
173 public double probability(int x) {
174 final double logProbability = logProbability(x);
175 return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
176 }
177
178
179 @Override
180 public double logProbability(int x) {
181 double ret;
182
183 int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
184 if (x < domain[0] || x > domain[1]) {
185 ret = Double.NEGATIVE_INFINITY;
186 } else {
187 double p = (double) sampleSize / (double) populationSize;
188 double q = (double) (populationSize - sampleSize) / (double) populationSize;
189 double p1 = SaddlePointExpansion.logBinomialProbability(x,
190 numberOfSuccesses, p, q);
191 double p2 =
192 SaddlePointExpansion.logBinomialProbability(sampleSize - x,
193 populationSize - numberOfSuccesses, p, q);
194 double p3 =
195 SaddlePointExpansion.logBinomialProbability(sampleSize, populationSize, p, q);
196 ret = p1 + p2 - p3;
197 }
198
199 return ret;
200 }
201
202
203
204
205
206
207
208 public double upperCumulativeProbability(int x) {
209 double ret;
210
211 final int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
212 if (x <= domain[0]) {
213 ret = 1.0;
214 } else if (x > domain[1]) {
215 ret = 0.0;
216 } else {
217 ret = innerCumulativeProbability(domain[1], x, -1);
218 }
219
220 return ret;
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 private double innerCumulativeProbability(int x0, int x1, int dx) {
237 double ret = probability(x0);
238 while (x0 != x1) {
239 x0 += dx;
240 ret += probability(x0);
241 }
242 return ret;
243 }
244
245
246
247
248
249
250
251 @Override
252 public double getNumericalMean() {
253 return getSampleSize() * (getNumberOfSuccesses() / (double) getPopulationSize());
254 }
255
256
257
258
259
260
261
262
263 @Override
264 public double getNumericalVariance() {
265 return numericalVariance;
266 }
267
268
269
270
271
272
273 private double calculateNumericalVariance() {
274 final double N = getPopulationSize();
275 final double m = getNumberOfSuccesses();
276 final double n = getSampleSize();
277 return (n * m * (N - n) * (N - m)) / (N * N * (N - 1));
278 }
279
280
281
282
283
284
285
286
287
288
289 @Override
290 public int getSupportLowerBound() {
291 return FastMath.max(0,
292 getSampleSize() + getNumberOfSuccesses() - getPopulationSize());
293 }
294
295
296
297
298
299
300
301
302
303 @Override
304 public int getSupportUpperBound() {
305 return FastMath.min(getNumberOfSuccesses(), getSampleSize());
306 }
307
308
309
310
311
312
313
314
315 @Override
316 public boolean isSupportConnected() {
317 return true;
318 }
319 }