non_central_chi_squared.hpp

来自「Boost provides free peer-reviewed portab」· HPP 代码 · 共 965 行 · 第 1/3 页

HPP
965
字号
// boost\math\distributions\non_central_chi_squared.hpp// Copyright John Maddock 2008.// Use, modification and distribution are subject to the// Boost Software License, Version 1.0.// (See accompanying file LICENSE_1_0.txt// or copy at http://www.boost.org/LICENSE_1_0.txt)#ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP#define BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP#include <boost/math/distributions/fwd.hpp>#include <boost/math/special_functions/gamma.hpp> // for incomplete gamma. gamma_q#include <boost/math/special_functions/bessel.hpp> // for cyl_bessel_i#include <boost/math/special_functions/round.hpp> // for iround#include <boost/math/distributions/complement.hpp> // complements#include <boost/math/distributions/chi_squared.hpp> // central distribution#include <boost/math/distributions/detail/common_error_handling.hpp> // error checks#include <boost/math/special_functions/fpclassify.hpp> // isnan.#include <boost/math/tools/roots.hpp> // for root finding.#include <boost/math/distributions/detail/generic_mode.hpp>#include <boost/math/distributions/detail/generic_quantile.hpp>namespace boost{   namespace math   {      template <class RealType, class Policy>      class non_central_chi_squared_distribution;      namespace detail{         template <class T, class Policy>         T non_central_chi_square_q(T x, T f, T theta, const Policy& pol, T init_sum = 0)         {            //            // Computes the complement of the Non-Central Chi-Square            // Distribution CDF by summing a weighted sum of complements            // of the central-distributions.  The weighting factor is            // a Poisson Distribution.            //            // This is an application of the technique described in:            //            // Computing discrete mixtures of continuous            // distributions: noncentral chisquare, noncentral t            // and the distribution of the square of the sample            // multiple correlation coeficient.            // D. Benton, K. Krishnamoorthy.            // Computational Statistics & Data Analysis 43 (2003) 249 - 267            //            BOOST_MATH_STD_USING            // Special case:            if(x == 0)               return 1;            //            // Initialize the variables we'll be using:            //            T lambda = theta / 2;            T del = f / 2;            T y = x / 2;            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();            T errtol = ldexp(1.0, -boost::math::policies::digits<T, Policy>());            T sum = init_sum;            //            // k is the starting location for iteration, we'll            // move both forwards and backwards from this point.            // k is chosen as the peek of the Poisson weights, which            // will occur *before* the largest term.            //            int k = iround(lambda, pol);            // Forwards and backwards Poisson weights:            T poisf = boost::math::gamma_p_derivative(1 + k, lambda, pol);            T poisb = poisf * k / lambda;            // Initial forwards central chi squared term:            T gamf = boost::math::gamma_q(del + k, y, pol);            // Forwards and backwards recursion terms on the central chi squared:            T xtermf = boost::math::gamma_p_derivative(del + 1 + k, y, pol);            T xtermb = xtermf * (del + k) / y;            // Initial backwards central chi squared term:            T gamb = gamf - xtermb;            //            // Forwards iteration first, this is the            // stable direction for the gamma function            // recurrences:            //            int i;            for(i = k; static_cast<boost::uintmax_t>(i-k) < max_iter; ++i)            {               T term = poisf * gamf;               sum += term;               poisf *= lambda / (i + 1);               gamf += xtermf;               xtermf *= y / (del + i + 1);               if(((sum == 0) || (fabs(term / sum) < errtol)) && (term >= poisf * gamf))                  break;            }            //Error check:            if(static_cast<boost::uintmax_t>(i-k) >= max_iter)               policies::raise_evaluation_error(                  "cdf(non_central_chi_squared_distribution<%1%>, %1%)",                   "Series did not converge, closest value was %1%", sum, pol);            //            // Now backwards iteration: the gamma            // function recurrences are unstable in this            // direction, we rely on the terms deminishing in size            // faster than we introduce cancellation errors.              // For this reason it's very important that we start            // *before* the largest term so that backwards iteration            // is strictly converging.            //            for(i = k - 1; i >= 0; --i)            {               T term = poisb * gamb;               sum += term;               poisb *= i / lambda;               xtermb *= (del + i) / y;               gamb -= xtermb;               if((sum == 0) || (fabs(term / sum) < errtol))                  break;            }            return sum;         }         template <class T, class Policy>         T non_central_chi_square_p_ding(T x, T f, T theta, const Policy& pol, T init_sum = 0)         {            //            // This is an implementation of:            //            // Algorithm AS 275:             // Computing the Non-Central #2 Distribution Function            // Cherng G. Ding            // Applied Statistics, Vol. 41, No. 2. (1992), pp. 478-482.            //            // This uses a stable forward iteration to sum the            // CDF, unfortunately this can not be used for large            // values of the non-centrality parameter because:            // * The first term may underfow to zero.            // * We may need an extra-ordinary number of terms            //   before we reach the first *significant* term.            //            BOOST_MATH_STD_USING            // Special case:            if(x == 0)               return 0;            T tk = boost::math::gamma_p_derivative(f/2 + 1, x/2, pol);            T lambda = theta / 2;            T vk = exp(-lambda);            T uk = vk;            T sum = init_sum + tk * vk;            if(sum == 0)               return sum;            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();            T errtol = ldexp(1.0, -boost::math::policies::digits<T, Policy>());            int i;            T lterm(0), term(0);            for(i = 1; static_cast<boost::uintmax_t>(i) < max_iter; ++i)            {               tk = tk * x / (f + 2 * i);               uk = uk * lambda / i;               vk = vk + uk;               lterm = term;               term = vk * tk;               sum += term;               if((fabs(term / sum) < errtol) && (term <= lterm))                  break;            }            //Error check:            if(static_cast<boost::uintmax_t>(i) >= max_iter)               policies::raise_evaluation_error(                  "cdf(non_central_chi_squared_distribution<%1%>, %1%)",                   "Series did not converge, closest value was %1%", sum, pol);            return sum;         }         template <class T, class Policy>         T non_central_chi_square_p(T y, T n, T lambda, const Policy& pol, T init_sum)         {            //            // This is taken more or less directly from:            //            // Computing discrete mixtures of continuous            // distributions: noncentral chisquare, noncentral t            // and the distribution of the square of the sample            // multiple correlation coeficient.            // D. Benton, K. Krishnamoorthy.            // Computational Statistics & Data Analysis 43 (2003) 249 - 267            //            // We're summing a Poisson weighting term multiplied by            // a central chi squared distribution.            //            BOOST_MATH_STD_USING            // Special case:            if(y == 0)               return 0;            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();            T errtol = ldexp(1.0, -boost::math::policies::digits<T, Policy>());            T errorf(0), errorb(0);            T x = y / 2;            T del = lambda / 2;            //            // Starting location for the iteration, we'll iterate            // both forwards and backwards from this point.  The            // location chosen is the maximum of the Poisson weight            // function, which ocurrs *after* the largest term in the            // sum.            //            int k = iround(del, pol);            T a = n / 2 + k;            // Central chi squared term for forward iteration:            T gamkf = boost::math::gamma_p(a, x, pol);            if(lambda == 0)               return gamkf;            // Central chi squared term for backward iteration:            T gamkb = gamkf;            // Forwards Poisson weight:            T poiskf = gamma_p_derivative(k+1, del, pol);            // Backwards Poisson weight:            T poiskb = poiskf;            // Forwards gamma function recursion term:            T xtermf = boost::math::gamma_p_derivative(a, x, pol);            // Backwards gamma function recursion term:            T xtermb = xtermf * x / a;            T sum = init_sum + poiskf * gamkf;            if(sum == 0)               return sum;            int i = 1;            //            // Backwards recursion first, this is the stable            // direction for gamma function recurrences:            //            while(i <= k)             {               xtermb *= (a - i + 1) / x;               gamkb += xtermb;               poiskb = poiskb * (k - i + 1) / del;               errorf = errorb;               errorb = gamkb * poiskb;               sum += errorb;               if((fabs(errorb / sum) < errtol) && (errorb <= errorf))                  break;               ++i;            }            i = 1;            //            // Now forwards recursion, the gamma function            // recurrence relation is unstable in this direction,            // so we rely on the magnitude of successive terms            // decreasing faster than we introduce cancellation error.            // For this reason it's vital that k is chosen to be *after*            // the largest term, so that successive forward iterations            // are strictly (and rapidly) converging.            //            do            {               xtermf = xtermf * x / (a + i - 1);               gamkf = gamkf - xtermf;               poiskf = poiskf * del / (k + i);               errorf = poiskf * gamkf;               sum += errorf;               ++i;            }while((fabs(errorf / sum) > errtol) && (static_cast<boost::uintmax_t>(i) < max_iter));            //Error check:            if(static_cast<boost::uintmax_t>(i) >= max_iter)               policies::raise_evaluation_error(                  "cdf(non_central_chi_squared_distribution<%1%>, %1%)",                   "Series did not converge, closest value was %1%", sum, pol);            return sum;         }         template <class T, class Policy>         T non_central_chi_square_pdf(T x, T n, T lambda, const Policy& pol)         {            //            // As above but for the PDF:            //            BOOST_MATH_STD_USING            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();            T errtol = ldexp(1.0, -boost::math::policies::digits<T, Policy>());            T x2 = x / 2;            T n2 = n / 2;            T l2 = lambda / 2;            T sum = 0;            int k = itrunc(l2);            T pois = gamma_p_derivative(k + 1, l2, pol) * gamma_p_derivative(n2 + k, x2);            if(pois == 0)               return 0;            T poisb = pois;            for(int i = k; ; ++i)            {               sum += pois;               if(pois / sum < errtol)                  break;               if(static_cast<boost::uintmax_t>(i - k) >= max_iter)                  return policies::raise_evaluation_error(                     "pdf(non_central_chi_squared_distribution<%1%>, %1%)",                      "Series did not converge, closest value was %1%", sum, pol);               pois *= l2 * x2 / ((i + 1) * (n2 + i));            }            for(int i = k - 1; i >= 0; --i)            {               poisb *= (i + 1) * (n2 + i) / (l2 * x2);               sum += poisb;               if(poisb / sum < errtol)                  break;            }            return sum / 2;         }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?