LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
random_discrete.c
浏览该文件的文档.
1#include <math.h>
2#include "random_internal.h"
3
8
10static size_t poisson_inversion(uint64_t* state, double lambda) {
11 double L = exp(-lambda);
12 double p = 1.0;
13 size_t k = 0;
14
15 do {
16 k++;
17 p *= u64_to_double01(xoshiro256ss_next(state));
18 } while (p > L);
19
20 return k - 1;
21}
22
23static lmmc_status_t poisson_store_sample(double sample, size_t* out) {
24 const double exclusive_limit = (double)SIZE_MAX + 1.0;
25 if (!isfinite(sample) || sample < 0.0 || sample >= exclusive_limit) {
27 }
28 *out = (size_t)sample;
29 return LMMC_STATUS_OK;
30}
31
37static lmmc_status_t poisson_ptrd(uint64_t* state, double lambda, size_t* out) {
38 double smu = sqrt(lambda);
39 double b = 0.931 + 2.53 * smu;
40 double a = -0.059 + 0.02483 * b;
41 double inv_alpha = 1.1239 + 1.1328 / (b - 3.4);
42 double vr = 0.9277 - 3.6224 / (b - 2.0);
43 double us, v, u, k_real;
44
45 for (;;) {
46 v = u64_to_double01(xoshiro256ss_next(state));
47 if (v <= 0.86 * vr) {
48 u = v / vr - 0.43;
49 k_real = floor((2.0 * a / (0.5 - fabs(u)) + b) * u + lambda + 0.445);
50 if (k_real >= 0.0) {
51 return poisson_store_sample(k_real, out);
52 }
53 }
54
55 if (v >= vr) {
56 u = u64_to_double01(xoshiro256ss_next(state)) - 0.5;
57 } else {
58 u = v / vr - 0.93;
59 u = ((u >= 0.0) ? 0.5 : -0.5) - u;
60 v = u64_to_double01(xoshiro256ss_next(state)) * vr;
61 }
62
63 us = 0.5 - fabs(u);
64 if (us == 0.0 || (us < 0.013 && v > us)) {
65 continue;
66 }
67
68 k_real = floor((2.0 * a / us + b) * u + lambda + 0.445);
69 if (!isfinite(k_real)) {
71 }
72 if (k_real < 0.0) {
73 continue;
74 }
75
81 v = v * inv_alpha / (a / (us * us) + b);
82 {
83 const double log_accept = k_real * log(lambda) - lambda - lgamma(k_real + 1.0);
84 if (!isfinite(log_accept)) {
86 }
87 if (log(v) <= log_accept) {
88 return poisson_store_sample(k_real, out);
89 }
90 }
91 }
92}
93
95 lmmc_rng_t* rng,
96 lmmc_real_t lambda,
97 size_t* out)
98{
99 if (rng == NULL || out == NULL) {
101 }
102 if (!isfinite(lambda) || lambda <= 0.0) {
104 }
105
106 if (lambda < 10.0) {
107 *out = poisson_inversion(rng->state, lambda);
108 } else {
109 return poisson_ptrd(rng->state, lambda, out);
110 }
111
112 return LMMC_STATUS_OK;
113}
114
115
121static uint64_t binomial_inversion(uint64_t* state, uint64_t n, double p) {
122 const double q = 1.0 - p;
123 const double qn = exp((double)n * log1p(-p));
124 const double np = (double)n * p;
125 const uint64_t bound = (uint64_t)fmin((double)n, np + 10.0 * sqrt(np * q + 1.0));
126 uint64_t x = 0;
127 double px = qn;
128 double u = u64_to_double01(xoshiro256ss_next(state));
129
130 while (u > px) {
131 ++x;
132 if (x > bound) {
133 x = 0;
134 px = qn;
135 u = u64_to_double01(xoshiro256ss_next(state));
136 } else {
137 u -= px;
138 px = (((double)(n - x + 1) * p) * px) / ((double)x * q);
139 }
140 }
141 return x;
142}
143
144static int binomial_btpe_accept(
145 uint64_t n, double r, double q, int64_t m, double nrq,
146 double xm, int64_t y, double v) {
147 const uint64_t k = y >= m ? (uint64_t)y - (uint64_t)m : (uint64_t)m - (uint64_t)y;
148 if (k <= 20 || (double)k >= nrq / 2.0 - 1.0) {
149 const double s = r / q;
150 const double aa = s * ((double)n + 1.0);
151 double f = 1.0;
152 int64_t i;
153 if (m < y) {
154 for (i = m + 1; i <= y; ++i) {
155 f *= aa / (double)i - s;
156 }
157 } else if (m > y) {
158 for (i = y + 1; i <= m; ++i) {
159 f /= aa / (double)i - s;
160 }
161 }
162 if (v > f) {
163 return 0;
164 }
165 } else {
166 const double kd = (double)k;
167 const double rho = (kd / nrq) *
168 ((kd * (kd / 3.0 + 0.625) + 1.0 / 6.0) / nrq + 0.5);
169 const double t = -kd * kd / (2.0 * nrq);
170 const double logv = log(v);
171 if (logv > t - rho) {
172 const double x1 = (double)y + 1.0;
173 const double f1 = (double)m + 1.0;
174 const double z = (double)n + 1.0 - (double)m;
175 const double w = (double)n - (double)y + 1.0;
176 const double x2 = x1 * x1, f2 = f1 * f1, z2 = z * z, w2 = w * w;
177 double accept;
178 if (logv > t + rho) {
179 return 0;
180 }
181 accept = xm * log(f1 / x1) + ((double)n - (double)m + 0.5) * log(z / w)
182 + ((double)y - (double)m) * log(w * r / (x1 * q))
183 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / f2) / f2) / f2) / f2) / f1 / 166320.0
184 + (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / z2) / z2) / z2) / z2) / z / 166320.0
185 - (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / x2) / x2) / x2) / x2) / x1 / 166320.0
186 - (13860.0 - (462.0 - (132.0 - (99.0 - 140.0 / w2) / w2) / w2) / w2) / w / 166320.0;
187 if (logv > accept) {
188 return 0;
189 }
190 }
191 }
192 return 1;
193}
194
195static uint64_t binomial_btpe(uint64_t* state, uint64_t n, double p) {
196 const double r = fmin(p, 1.0 - p);
197 const double q = 1.0 - r;
198 const double fm = (double)n * r + r;
199 const int64_t m = (int64_t)floor(fm);
200 const double p1 = floor(2.195 * sqrt((double)n * r * q) - 4.6 * q) + 0.5;
201 const double xm = (double)m + 0.5;
202 const double xl = xm - p1;
203 const double xr = xm + p1;
204 const double c = 0.134 + 20.5 / (15.3 + (double)m);
205 double a = (fm - xl) / (fm - xl * r);
206 const double laml = a * (1.0 + a / 2.0);
207 double lamr;
208 const double p2 = p1 * (1.0 + 2.0 * c);
209 const double p3 = p2 + c / laml;
210 double p4;
211 const double nrq = (double)n * r * q;
212
213 a = (xr - fm) / (xr * q);
214 lamr = a * (1.0 + a / 2.0);
215 p4 = p3 + c / lamr;
216
217 for (;;) {
218 double u = u64_to_double01(xoshiro256ss_next(state)) * p4;
219 double v = u64_to_double01(xoshiro256ss_next(state));
220 double x;
221 int64_t y;
222
223 if (u <= p1) {
224 uint64_t result;
225 y = (int64_t)floor(xm - p1 * v + u);
226 result = (uint64_t)y;
227 return p > 0.5 ? n - result : result;
228 } else if (u <= p2) {
229 x = xl + (u - p1) / c;
230 v = v * c + 1.0 - fabs((double)m - x + 0.5) / p1;
231 if (v > 1.0) {
232 continue;
233 }
234 y = (int64_t)floor(x);
235 } else if (u <= p3) {
236 if (v == 0.0) {
237 continue;
238 }
239 y = (int64_t)floor(xl + log(v) / laml);
240 if (y < 0) {
241 continue;
242 }
243 v = v * (u - p2) * laml;
244 } else {
245 if (v == 0.0) {
246 continue;
247 }
248 y = (int64_t)floor(xr - log(v) / lamr);
249 if (y < 0 || (uint64_t)y > n) {
250 continue;
251 }
252 v = v * (u - p3) * lamr;
253 }
254
255 if (!binomial_btpe_accept(n, r, q, m, nrq, xm, y, v)) {
256 continue;
257 }
258 {
259 uint64_t result = (uint64_t)y;
260 return p > 0.5 ? n - result : result;
261 }
262 }
263}
264
265static uint64_t binomial_chunk(uint64_t* state, uint64_t n, double p) {
266 const double working_p = p <= 0.5 ? p : 1.0 - p;
267 uint64_t value;
268 if ((double)n * working_p < 30.0) {
269 value = binomial_inversion(state, n, working_p);
270 return p <= 0.5 ? value : n - value;
271 }
272 return binomial_btpe(state, n, p);
273}
274
276 lmmc_rng_t* rng,
277 size_t n,
278 lmmc_real_t p,
279 size_t* out
280) {
281 const uint64_t max_chunk = (UINT64_C(1) << 53) - UINT64_C(1);
282 uint64_t remaining;
283 uint64_t total = 0;
284 if (rng == NULL || out == NULL || !isfinite(p) || p < 0.0 || p > 1.0) {
286 }
287 if (n == 0 || p == 0.0) {
288 *out = 0;
289 return LMMC_STATUS_OK;
290 }
291 if (p == 1.0) {
292 *out = n;
293 return LMMC_STATUS_OK;
294 }
295
296 remaining = (uint64_t)n;
297 while (remaining != 0) {
298 uint64_t chunk = remaining > max_chunk ? max_chunk : remaining;
299 total += binomial_chunk(rng->state, chunk, p);
300 remaining -= chunk;
301 }
302 *out = (size_t)total;
303 return LMMC_STATUS_OK;
304}
double lmmc_real_t
Public and internal LMMC real type.
lmmc_status_t lmmc_rng_poisson(lmmc_rng_t *rng, lmmc_real_t lambda, size_t *out)
生成 Poisson 分布样本 。
lmmc_status_t lmmc_rng_binomial(lmmc_rng_t *rng, size_t n, lmmc_real_t p, size_t *out)
生成二项分布样本 Binomial(n, p)。
uint64_t state[4]
随机数发生器(不透明类型)。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_INVALID_ARGUMENT