/* cdf/hypergeometric.c
*
* Copyright (C) 2004 Free Software Foundation, Inc.
* Written by Jason H. Stover.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* Computes the cumulative distribution function for a hypergeometric
* random variable. A hypergeometric random variable X is the number
* of elements of type 0 in a sample of size t, drawn from a population
* of size n1 + n0, in which n1 are of type 1 and n0 are of type 0.
*
* This algorithm computes Pr( X <= k ) by summing the terms from
* the mass function, Pr( X = k ).
*
* References:
*
* T. Wu. An accurate computation of the hypergeometric distribution
* function. ACM Transactions on Mathematical Software. Volume 19, number 1,
* March 1993.
* This algorithm is not used, since it requires factoring the
* numerator and denominator, then cancelling. It is more accurate
* than the algorithm used here, but the cancellation requires more
* time than the algorithm used here.
*
* W. Feller. An Introduction to Probability Theory and Its Applications,
* third edition. 1968. Chapter 2, section 6.
*/
#include <config.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_randist.h>
#include "gsl-extras.h"
/*
* Pr (X <= k)
*/
double
gslextras_cdf_hypergeometric_P (const unsigned int k,
const unsigned int n0,
const unsigned int n1,
const unsigned int t)
{
unsigned int i;
unsigned int mode;
double P;
double tmp;
double relerr;
if( t > (n0+n1))
{
GSLEXTRAS_CDF_ERROR("t larger than population size",GSL_EDOM);
}
else if( k >= n0 || k >= t)
{
P = 1.0;
}
else if (k < 0.0)
{
P = 0.0;
}
else
{
P = 0.0;
mode = (int) t*n0 / (n0+n1);
relerr = 1.0;
if( k < mode )
{
i = k;
relerr = 1.0;
while(i != UINT_MAX && relerr > GSL_DBL_EPSILON && P < 1.0)
{
tmp = gsl_ran_hypergeometric_pdf(i, n0, n1, t);
P += tmp;
relerr = tmp / P;
i--;
}
}
else
{
i = mode;
relerr = 1.0;
while(i <= k && relerr > GSL_DBL_EPSILON && P < 1.0)
{
tmp = gsl_ran_hypergeometric_pdf(i, n0, n1, t);
P += tmp;
relerr = tmp / P;
i++;
}
i = mode - 1;
relerr = 1.0;
while( i != UINT_MAX && relerr > GSL_DBL_EPSILON && P < 1.0)
{
tmp = gsl_ran_hypergeometric_pdf(i, n0, n1, t);
P += tmp;
relerr = tmp / P;
i--;
}
}
/*
* Hack to get rid of a pesky error when the sum
* gets slightly above 1.0.
*/
P = GSL_MIN_DBL (P, 1.0);
}
return P;
}
/*
* Pr (X > k)
*/
double
gslextras_cdf_hypergeometric_Q (const unsigned int k,
const unsigned int n0,
const unsigned int n1,
const unsigned int t)
{
unsigned int i;
unsigned int mode;
double P;
double relerr;
double tmp;
if( t > (n0+n1))
{
GSLEXTRAS_CDF_ERROR("t larger than population size",GSL_EDOM);
}
else if( k >= n0 || k >= t)
{
P = 0.0;
}
else if (k < 0.0)
{
P = 1.0;
}
else
{
P = 0.0;
mode = (int) t*n0 / (n0+n1);
relerr = 1.0;
if(k < mode)
{
i = mode;
while( i <= t && relerr > GSL_DBL_EPSILON && P < 1.0)
{
tmp = gsl_ran_hypergeometric_pdf(i, n0, n1, t);
P += tmp;
relerr = tmp / P;
i++;
}
i = mode - 1;
relerr = 1.0;
while ( i > k && relerr > GSL_DBL_EPSILON && P < 1.0)
{
tmp = gsl_ran_hypergeometric_pdf(i, n0, n1, t);
P += tmp;
relerr = tmp / P;
i--;
}
}
else
{
i = k+1;
while(i <= t && relerr > GSL_DBL_EPSILON && P < 1.0)
{
tmp = gsl_ran_hypergeometric_pdf(i, n0, n1, t);
P += tmp;
relerr = tmp / P;
i++;
}
}
/*
* Hack to get rid of a pesky error when the sum
* gets slightly above 1.0.
*/
P = GSL_MIN_DBL(P, 1.0);
}
return P;
}
syntax highlighted by Code2HTML, v. 0.9.1