Rev 5458 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** Mathlib : A C Library of Special Functions* Copyright (C) 1998 Ross Ihaka** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.** SYNOPSIS** #include "Mathlib.h"* double rhyper(double NR, double NB, double n);** DESCRIPTION** Random variates from the hypergeometric distribution.* Returns the number of white balls drawn when kk balls* are drawn at random from an urn containing nn1 white* and nn2 black balls.** REFERENCE** V. Kachitvichyanukul and B. Schmeiser (1985).* ``Computer generation of hypergeometric random variates,''* Journal of Statistical Computation and Simulation 22, 127-145.*/#include "Mathlib.h"/* afc(i) := ln( i! ) [logarithm of the factorial i.* If (i > 7), use Stirling's approximation, otherwise use table lookup.*/static double al[9] ={0.0,0.0,/*ln(0!)=ln(1)*/0.0,/*ln(1!)=ln(1)*/0.69314718055994530941723212145817,/*ln(2) */1.79175946922805500081247735838070,/*ln(6) */3.17805383034794561964694160129705,/*ln(24)*/4.78749174278204599424770093452324,6.57925121201010099506017829290394,8.52516136106541430016553103634712/*, 10.60460290274525022841722740072165*/};static double afc(int i){double di, value;if (i < 0) {MATHLIB_WARNING("rhyper.c: afc(i), i=%d < 0 -- SHOULD NOT HAPPEN!\n",i);return -1;/* unreached (Wall) */} else if (i <= 7) {value = al[i + 1];} else {di = i;value = (di + 0.5) * log(di) - di + 0.08333333333333 / di- 0.00277777777777 / di / di / di + 0.9189385332;}return value;}double rhyper(double nn1in, double nn2in, double kkin){int nn1, nn2, kk;static int ks = -1;static int n1s = -1;static int n2s = -1;static double con = 57.56462733;static double deltal = 0.0078;static double deltau = 0.0034;static double scale = 1e25;static double a;static double d, e, f, g;static int i, k, m;static double p;static double r, s, t;static double u, v, w;static double lamdl, y, lamdr;static int minjx, maxjx, n1, n2;static double p1, p2, p3, y1, de, dg;static int setup1, setup2;static double gl, kl, ub, nk, dr, nm, gu, kr, ds, dt;static int ix;static double tn;static double xl;static double ym, yn, yk, xm;static double xr;static double xn;static int reject;static double xk;/* extern double afc(int); */static double alv;/* check parameter validity */#ifdef IEEE_754if(!R_FINITE(nn1in) || !R_FINITE(nn2in) || !R_FINITE(kkin)) {ML_ERROR(ME_DOMAIN);return ML_NAN;}#endifnn1 = floor(nn1in+0.5);nn2 = floor(nn2in+0.5);kk = floor(kkin +0.5);if (nn1 < 0 || nn2 < 0 || kk < 0 || kk > nn1 + nn2) {ML_ERROR(ME_DOMAIN);return ML_NAN;}/* if new parameter values, initialize */reject = LTRUE;setup1 = LFALSE;setup2 = LFALSE;if (nn1 != n1s || nn2 != n2s) {setup1 = LTRUE;setup2 = LTRUE;} else if (kk != ks) {setup2 = LTRUE;}if (setup1) {n1s = nn1;n2s = nn2;tn = nn1 + nn2;if (nn1 <= nn2) {n1 = nn1;n2 = nn2;} else {n1 = nn2;n2 = nn1;}}if (setup2) {ks = kk;if (kk + kk >= tn) {k = tn - kk;} else {k = kk;}}if (setup1 || setup2) {m = (k + 1.0) * (n1 + 1.0) / (tn + 2.0);minjx = imax2(0, k - n2);maxjx = imin2(n1, k);}/* generate random variate --- Three basic cases */if (minjx == maxjx) { /* I: degenerate distribution ---------------- */ix = maxjx;/* return ix;No, need to unmangle <TSL>*//* return appropriate variate */if (kk + kk >= tn) {if (nn1 > nn2) {ix = kk - nn2 + ix;} else {ix = nn1 - ix;}} else {if (nn1 > nn2)ix = kk - ix;}return ix;} else if (m - minjx < 10) { /* II: inverse transformation ---------- */if (setup1 || setup2) {if (k < n2) {w = exp(con + afc(n2) + afc(n1 + n2 - k)- afc(n2 - k) - afc(n1 + n2));} else {w = exp(con + afc(n1) + afc(k)- afc(k - n2) - afc(n1 + n2));}}L10:p = w;ix = minjx;u = sunif() * scale;L20:if (u > p) {u = u - p;p = p * (n1 - ix) * (k - ix);ix = ix + 1;p = p / ix / (n2 - k + ix);if (ix > maxjx)goto L10;goto L20;}} else { /* III : h2pe --------------------------------------------- */if (setup1 || setup2) {s = sqrt((tn - k) * k * n1 * n2 / (tn - 1) / tn / tn);/* remark: d is defined in reference without int. *//* the truncation centers the cell boundaries at 0.5 */d = (int) (1.5 * s) + .5;xl = m - d + .5;xr = m + d + .5;a = afc(m) + afc(n1 - m) + afc(k - m)+ afc(n2 - k + m);kl = exp(a - afc((int) (xl)) - afc((int) (n1 - xl))- afc((int) (k - xl))- afc((int) (n2 - k + xl)));kr = exp(a - afc((int) (xr - 1))- afc((int) (n1 - xr + 1))- afc((int) (k - xr + 1))- afc((int) (n2 - k + xr - 1)));lamdl = -log(xl * (n2 - k + xl) / (n1 - xl + 1)/ (k - xl + 1));lamdr = -log((n1 - xr + 1) * (k - xr + 1)/ xr / (n2 - k + xr));p1 = d + d;p2 = p1 + kl / lamdl;p3 = p2 + kr / lamdr;}L30:u = sunif() * p3;v = sunif();if (u < p1) { /* rectangular region */ix = xl + u;} else if (u <= p2) { /* left tail */ix = xl + log(v) / lamdl;if (ix < minjx)goto L30;v = v * (u - p1) * lamdl;} else { /* right tail */ix = xr - log(v) / lamdr;if (ix > maxjx)goto L30;v = v * (u - p2) * lamdr;}/* acceptance/rejection test */if (m < 100 || ix <= 50) {/* explicit evaluation */f = 1.0;if (m < ix) {for (i = m + 1; i <= ix; i++)f = f * (n1 - i + 1) * (k - i + 1)/ (n2 - k + i) / i;} else if (m > ix) {for (i = ix + 1; i <= m; i++)f = f * i * (n2 - k + i) / (n1 - i)/ (k - i);}if (v <= f) {reject = LFALSE;}} else {/* squeeze using upper and lower bounds */y = ix;y1 = y + 1.0;ym = y - m;yn = n1 - y + 1.0;yk = k - y + 1.0;nk = n2 - k + y1;r = -ym / y1;s = ym / yn;t = ym / yk;e = -ym / nk;g = yn * yk / (y1 * nk) - 1.0;dg = 1.0;if (g < 0.0)dg = 1.0 + g;gu = g * (1.0 + g * (-0.5 + g / 3.0));gl = gu - .25 * (g * g * g * g) / dg;xm = m + 0.5;xn = n1 - m + 0.5;xk = k - m + 0.5;nm = n2 - k + xm;ub = y * gu - m * gl + deltau+ xm * r * (1. + r * (-0.5 + r / 3.0))+ xn * s * (1. + s * (-0.5 + s / 3.0))+ xk * t * (1. + t * (-0.5 + t / 3.0))+ nm * e * (1. + e * (-0.5 + e / 3.0));/* test against upper bound */alv = log(v);if (alv > ub) {reject = LTRUE;} else {/* test against lower bound */dr = xm * (r * r * r * r);if (r < 0.0)dr = dr / (1.0 + r);ds = xn * (s * s * s * s);if (s < 0.0)ds = ds / (1.0 + s);dt = xk * (t * t * t * t);if (t < 0.0)dt = dt / (1.0 + t);de = nm * (e * e * e * e);if (e < 0.0)de = de / (1.0 + e);if (alv < ub - 0.25 * (dr + ds + dt + de)+ (y + m) * (gl - gu) - deltal) {reject = LFALSE;} else {/** stirling's formula to machine* accuracy*/if (alv <= (a - afc(ix) - afc(n1 - ix)- afc(k - ix) - afc(n2 - k + ix))) {reject = LFALSE;} else {reject = LTRUE;}}}}if (reject)goto L30;}/* return appropriate variate */if (kk + kk >= tn) {if (nn1 > nn2) {ix = kk - nn2 + ix;} else {ix = nn1 - ix;}} else {if (nn1 > nn2)ix = kk - ix;}return ix;}