1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.math.distribution; 18 19 import java.io.Serializable; 20 21 import org.apache.commons.math.MathException; 22 23 /** 24 * The default implementation of {@link ChiSquaredDistribution} 25 * 26 * @version $Revision: 617953 $ $Date: 2008-02-02 22:54:00 -0700 (Sat, 02 Feb 2008) $ 27 */ 28 public class ChiSquaredDistributionImpl 29 extends AbstractContinuousDistribution 30 implements ChiSquaredDistribution, Serializable { 31 32 /** Serializable version identifier */ 33 private static final long serialVersionUID = -8352658048349159782L; 34 35 /** Internal Gamma distribution. */ 36 private GammaDistribution gamma; 37 38 /** 39 * Create a Chi-Squared distribution with the given degrees of freedom. 40 * @param df degrees of freedom. 41 */ 42 public ChiSquaredDistributionImpl(double df) { 43 this(df, new GammaDistributionImpl(df / 2.0, 2.0)); 44 } 45 46 /** 47 * Create a Chi-Squared distribution with the given degrees of freedom. 48 * @param df degrees of freedom. 49 * @param g the underlying gamma distribution used to compute probabilities. 50 * @since 1.2 51 */ 52 public ChiSquaredDistributionImpl(double df, GammaDistribution g) { 53 super(); 54 setGamma(g); 55 setDegreesOfFreedom(df); 56 } 57 58 /** 59 * Modify the degrees of freedom. 60 * @param degreesOfFreedom the new degrees of freedom. 61 */ 62 public void setDegreesOfFreedom(double degreesOfFreedom) { 63 getGamma().setAlpha(degreesOfFreedom / 2.0); 64 } 65 66 /** 67 * Access the degrees of freedom. 68 * @return the degrees of freedom. 69 */ 70 public double getDegreesOfFreedom() { 71 return getGamma().getAlpha() * 2.0; 72 } 73 74 /** 75 * For this disbution, X, this method returns P(X < x). 76 * @param x the value at which the CDF is evaluated. 77 * @return CDF for this distribution. 78 * @throws MathException if the cumulative probability can not be 79 * computed due to convergence or other numerical errors. 80 */ 81 public double cumulativeProbability(double x) throws MathException { 82 return getGamma().cumulativeProbability(x); 83 } 84 85 /** 86 * For this distribution, X, this method returns the critical point x, such 87 * that P(X < x) = <code>p</code>. 88 * <p> 89 * Returns 0 for p=0 and <code>Double.POSITIVE_INFINITY</code> for p=1.</p> 90 * 91 * @param p the desired probability 92 * @return x, such that P(X < x) = <code>p</code> 93 * @throws MathException if the inverse cumulative probability can not be 94 * computed due to convergence or other numerical errors. 95 * @throws IllegalArgumentException if <code>p</code> is not a valid 96 * probability. 97 */ 98 public double inverseCumulativeProbability(final double p) 99 throws MathException { 100 if (p == 0) { 101 return 0d; 102 } 103 if (p == 1) { 104 return Double.POSITIVE_INFINITY; 105 } 106 return super.inverseCumulativeProbability(p); 107 } 108 109 /** 110 * Access the domain value lower bound, based on <code>p</code>, used to 111 * bracket a CDF root. This method is used by 112 * {@link #inverseCumulativeProbability(double)} to find critical values. 113 * 114 * @param p the desired probability for the critical value 115 * @return domain value lower bound, i.e. 116 * P(X < <i>lower bound</i>) < <code>p</code> 117 */ 118 protected double getDomainLowerBound(double p) { 119 return Double.MIN_VALUE * getGamma().getBeta(); 120 } 121 122 /** 123 * Access the domain value upper bound, based on <code>p</code>, used to 124 * bracket a CDF root. This method is used by 125 * {@link #inverseCumulativeProbability(double)} to find critical values. 126 * 127 * @param p the desired probability for the critical value 128 * @return domain value upper bound, i.e. 129 * P(X < <i>upper bound</i>) > <code>p</code> 130 */ 131 protected double getDomainUpperBound(double p) { 132 // NOTE: chi squared is skewed to the left 133 // NOTE: therefore, P(X < μ) > .5 134 135 double ret; 136 137 if (p < .5) { 138 // use mean 139 ret = getDegreesOfFreedom(); 140 } else { 141 // use max 142 ret = Double.MAX_VALUE; 143 } 144 145 return ret; 146 } 147 148 /** 149 * Access the initial domain value, based on <code>p</code>, used to 150 * bracket a CDF root. This method is used by 151 * {@link #inverseCumulativeProbability(double)} to find critical values. 152 * 153 * @param p the desired probability for the critical value 154 * @return initial domain value 155 */ 156 protected double getInitialDomain(double p) { 157 // NOTE: chi squared is skewed to the left 158 // NOTE: therefore, P(X < μ) > .5 159 160 double ret; 161 162 if (p < .5) { 163 // use 1/2 mean 164 ret = getDegreesOfFreedom() * .5; 165 } else { 166 // use mean 167 ret = getDegreesOfFreedom(); 168 } 169 170 return ret; 171 } 172 173 /** 174 * Modify the underlying gamma distribution. The caller is responsible for 175 * insuring the gamma distribution has the proper parameter settings. 176 * @param g the new distribution. 177 * @since 1.2 made public 178 */ 179 public void setGamma(GammaDistribution g) { 180 this.gamma = g; 181 182 } 183 184 /** 185 * Access the Gamma distribution. 186 * @return the internal Gamma distribution. 187 */ 188 private GammaDistribution getGamma() { 189 return gamma; 190 } 191 }