/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
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.
*/
package org.geogebra.common.kernel.statistics;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.SetRandomValue;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.util.MyMath2;
/**
* Computes RandomNormal[a, b]
*
* @author Michael Borcherds
*/
public class AlgoRandomPoisson extends AlgoElement implements SetRandomValue {
protected GeoNumberValue a; // input
protected GeoNumeric num; // output
public AlgoRandomPoisson(Construction cons, String label,
GeoNumberValue a) {
super(cons);
this.a = a;
// output is random number
num = new GeoNumeric(cons);
cons.addRandomGeo(num);
setInputOutput(); // for AlgoElement
compute();
num.setLabel(label);
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[1];
input[0] = a.toGeoElement();
setOnlyOutput(num);
setDependencies(); // done by AlgoElement
}
public GeoNumeric getResult() {
return num;
}
@Override
public Commands getClassName() {
return Commands.RandomPoisson;
}
@Override
public final void compute() {
if (input[0].isDefined()) {
double lambda = a.getDouble();
if (lambda > 0) {
num.setValue(randomPoissonTRS(lambda));
} else {
num.setUndefined();
}
} else {
num.setUndefined();
}
}
/*
* poisson random number (Knuth)
*/
private int randomPoisson(double lambda) {
double L = Math.exp(-lambda);
double p = 1;
int k = 0;
do {
k++;
p *= kernel.getApplication().getRandomNumber();
} while (p >= L);
return k - 1;
}
/*
*
* Hermann, Wolfgang: The transformed rejection method for generating
* Poisson random variables Algorithm PTRS
* http://statmath.wu-wien.ac.at/papers/92-04-13.wh.ps.gz
* http://epub.wu-wien
* .ac.at/dyn/virlib/wp/eng/mediate/epub-wu-01_6f2.pdf?ID=epub-wu-01_6f2
*/
private int randomPoissonTRS(double mu) {
if (mu < 10) {
return randomPoisson(mu);
}
double b = 0.931 + +2.53 * Math.sqrt(mu);
double a1 = -0.059 + 0.02438 * b;
double v_r = 0.9277 - 3.6224 / (b - 2);
double us = 0;
double v = 1;
while (true) {
int k = -1;
while (k < 0 || (us < 0.013 && v > us)) {
double u = kernel.getApplication().getRandomNumber() - 0.5;
v = kernel.getApplication().getRandomNumber();
us = 0.5 - Math.abs(u);
k = (int) Math.floor((2 * a1 / us + b) * u + mu + 0.43);
if (us >= 0.07 && v < v_r) {
return k;
}
}
double alpha = 1.1239 + 1.1328 / (b - 3.4);
double lnmu = Math.log(mu);
v = v * alpha / (a1 / (us * us) + b);
if (Math.log(v * alpha / (a1 / us / us + b)) <= -mu + k * lnmu
- logOfKFactorial(k)) {
return k;
}
}
}
private static double halflog2pi = 0.5 * Math.log(2 * Math.PI);
private static double logtable[] = new double[10];
private static double logOfKFactorial(int k) {
if (k < 10) {
if (logtable[k] == 0) {
logtable[k] = Math.log(MyMath2.factorial(k));
}
return logtable[k];
}
// Stirling approximation
return halflog2pi + (k + 0.5) * Math.log(k + 1) - (k + 1)
+ (1 / 12.0 - (1 / 360.0 - 1 / 1260.0 / (k + 1) / (k + 1))
/ (k + 1) / (k + 1)) / (k + 1);
}
@Override
public void setRandomValue(double d0) {
double d = Math.round(Kernel.checkInteger(d0));
if (d >= 0) {
num.setValue(d);
num.updateRepaint();
}
}
}