package umontreal.iro.lecuyer.probdist;

import umontreal.iro.lecuyer.functions.MathFunctionUtil;
import umontreal.iro.lecuyer.functions.MathFunction;


/**
 * This container class takes an arbitrary continuous distribution and truncates
 * it to an interval [a, b], where a and b can be finite or infinite.
 * If the original density and distribution function are f0 and F0,
 * the new ones are f and F, defined by
 * 

*

*
* f (x) = f0(x)/(F0(b) - F0(a))         for a <= x <= b *

* and f (x) = 0 elsewhere, and *

*

*
* F(x) = (F0(x) - F0(a))/(F0(b) - F0(a))         for a <= x <= b. *

* The inverse distribution function of the truncated distribution is *

*

*
* F-1(u) = F0-1(F0(a) + (F0(b) - F0(a))u) *

* where F0-1 is the inverse distribution function of the * original distribution. * */ public class TruncatedDist extends ContinuousDistribution { public static int NUMINTERVALS = 500; private ContinuousDistribution dist0; // The original (non-truncated) dist. private double fa; // F(a) private double fb; // F(b) private double barfb; // bar F(b) private double fbfa; // F(b) - F(a) private double a; private double b; private double approxMean; private double approxVariance; private double approxStandardDeviation; /** * Constructs a new distribution by truncating distribution dist * to the interval [a, b]. Restrictions: a and b must be finite. * */ public TruncatedDist (ContinuousDistribution dist, double a, double b) { setParams (dist, a, b); } public double density (double x) { if ((x < a) || (x > b)) return 0; return dist0.density (x) / fbfa; } public double cdf (double x) { if (x <= a) return 0; else if (x >= b) return 1; else return (dist0.cdf (x) - fa) / fbfa; } public double barF (double x) { if (x <= a) return 1; else if (x >= b) return 0; else return (dist0.barF (x) - barfb) / fbfa; } public double inverseF (double u) { if (u == 0) return a; if (u == 1) return b; return dist0.inverseF (fa + fbfa * u); } /** * Returns an approximation of the mean computed with the * Simpson 1/3 numerical integration rule. * * @exception UnsupportedOperationException the mean of the truncated distribution is unknown * * */ public double getMean() { if (Double.isNaN (approxMean)) throw new UnsupportedOperationException("Undefined mean"); return approxMean; } /** * Returns an approximation of the variance computed with the * Simpson 1/3 numerical integration rule. * * @exception UnsupportedOperationException the mean of the truncated distribution is unknown * * */ public double getVariance() { if (Double.isNaN (approxVariance)) throw new UnsupportedOperationException("Unknown variance"); return approxVariance; } /** * Returns the square root of the approximate variance. * * @exception UnsupportedOperationException the mean of the truncated distribution is unknown * * */ public double getStandardDeviation() { if (Double.isNaN (approxStandardDeviation)) throw new UnsupportedOperationException("Unknown standard deviation"); return approxStandardDeviation; } /** * Returns the value of a. * */ public double getA() { return a; } /** * Returns the value of b. * */ public double getB() { return b; } /** * Returns the value of F0(a). * */ public double getFa() { return fa; } /** * Returns the value of F0(b). * */ public double getFb() { return fb; } /** * Returns the value of * F0(b) - F0(a), * the area under the truncated density function. * */ public double getArea() { return fbfa; } /** * Sets the parameters dist, a and b for this object. See the * constructor for details. * */ public void setParams (ContinuousDistribution dist, double a, double b) { if (a >= b) throw new IllegalArgumentException ("a must be smaller than b."); this.dist0 = dist; if (a <= dist.getXinf()) a = dist.getXinf(); if (b >= dist.getXsup()) b = dist.getXsup(); supportA = this.a = a; supportB = this.b = b; fa = dist.cdf (a); fb = dist.cdf (b); fbfa = fb - fa; barfb = dist.barF (b); if (((a <= dist.getXinf()) && (b >= dist.getXsup())) || ((a == Double.NEGATIVE_INFINITY) && (b == Double.POSITIVE_INFINITY))) { approxMean = dist.getMean(); approxVariance = dist.getVariance(); approxStandardDeviation = dist.getStandardDeviation(); } else { // The mean is the integral of xf*(x) over [a, b]. MomentFunction func1 = new MomentFunction (dist, 1); approxMean = MathFunctionUtil.simpsonIntegral (func1, a, b, NUMINTERVALS) / fbfa; // Estimate the integral of (x-E[X])^2 f*(x) over [a, b] MomentFunction func2 = new MomentFunction (dist, 2, approxMean); approxVariance = MathFunctionUtil.simpsonIntegral (func2, a, b, NUMINTERVALS) / fbfa; approxStandardDeviation = Math.sqrt (approxVariance); } } /** * Return a table containing the parameters of the current distribution. * This table is put in order: [a, b, F0(a), F0(b), * F0(b) - F0(a)]. * */ public double[] getParams () { double[] retour = {a, b, fa, fb, fbfa}; return retour; } /** * Returns a String containing information about the current distribution. * */ public String toString () { return getClass().getSimpleName() + " : a = " + a + ", b = " + b + ", F(a) = " + fa + ", F(b) = " + fb + ", F(b)-F(a) = " + fbfa; } private static class MomentFunction implements MathFunction { private ContinuousDistribution dist; private int moment; private double offset; public MomentFunction (ContinuousDistribution dist, int moment) { this.dist = dist; this.moment = moment; offset = 0; } public MomentFunction (ContinuousDistribution dist, int moment, double offset) { this (dist, moment); this.offset = offset; } public double evaluate (double x) { double res = dist.density (x); final double offsetX = x - offset; for (int i = 0; i < moment; i++) res *= offsetX; return res; } } }

Ads help maintain this website.