package umontreal.iro.lecuyer.randvar;
import umontreal.iro.lecuyer.rng.*;
import umontreal.iro.lecuyer.probdist.*;
/**
* This class implements Chi random variate generators using
* the ratio of uniforms method with shift.
*
*/
public class ChiRatioOfUniformsGen extends ChiGen {
/**
* Creates a chi random variate generator with
* ν = nu degrees of freedom, using stream s.
*
*/
public ChiRatioOfUniformsGen (RandomStream s, int nu) {
super (s, null);
setParams (nu);
}
/**
* Create a new generator for the distribution dist,
* using stream s.
*
*/
public ChiRatioOfUniformsGen (RandomStream s, ChiDist dist) {
super (s, dist);
if (dist != null)
setParams (dist.getNu ());
}
public double nextDouble() {
return ratioOfUniforms (stream, nu);
}
public static double nextDouble (RandomStream s, int nu) {
if (nu <= 0)
throw new IllegalArgumentException ("nu <= 0");
return ratioOfUniforms (s, nu);
}
//>>>>>>>>>>>>>>>>>>>> P R I V A T E M E T H O D S <<<<<<<<<<<<<<<<<<<<
//================================================================
// Method ratio of uniforms with shift.
// code taken and adapted from unuran
// file : /distributions/c_chi_gen.c
//===============================================================
private static double ratioOfUniforms (RandomStream stream, int nu) {
double u,v,z,zz,r;
if (nu == 1) {
while (true) {
u = stream.nextDouble();
v = stream.nextDouble() * 0.857763884960707;
z = v / u;
if (z < 0) continue;
zz = z * z;
r = 2.5 - zz;
if (z < 0.)
r = r + zz * z / (3. * z);
if (u < r * 0.3894003915)
break;
if (zz > (1.036961043 / u + 1.4))
continue;
if (2 * Math.log(u) < (- zz * 0.5 ))
break;
}
}
else { // nu > 1
final double b = Math.sqrt(nu - 1.);
final double vm1 = - 0.6065306597 * (1. - 0.25 / (b * b + 1.));
final double vm = (-b > vm1) ? -b : vm1;
final double vp = 0.6065306597 * (0.7071067812 + b) / (0.5 + b);
final double vd = vp - vm;
while (true) {
u = stream.nextDouble();
v = stream.nextDouble() * vd + vm;
z = v / u;
if (z < -b)
continue;
zz = z * z;
r = 2.5 - zz;
if (z < 0.0)
r = r + zz * z / (3.0 * (z + b));
if (u < r * 0.3894003915) {
z += b;
break;
}
if (zz > (1.036961043 / u + 1.4))
continue;
if (2. * Math.log(u) <
(Math.log(1.0 + z / b) * b * b - zz * 0.5 - z * b)) {
z += b;
break;
}
}
}
return z;
}
}
Ads help maintain this website.