beta.hpp

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

HPP
1,357
字号
//  (C) Copyright John Maddock 2006.//  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_BETA_HPP#define BOOST_MATH_SPECIAL_BETA_HPP#ifdef _MSC_VER#pragma once#endif#include <boost/math/special_functions/math_fwd.hpp>#include <boost/math/tools/config.hpp>#include <boost/math/special_functions/gamma.hpp>#include <boost/math/special_functions/factorials.hpp>#include <boost/math/special_functions/erf.hpp>#include <boost/math/special_functions/log1p.hpp>#include <boost/math/special_functions/expm1.hpp>#include <boost/math/special_functions/trunc.hpp>#include <boost/math/tools/roots.hpp>#include <boost/static_assert.hpp>#include <boost/config/no_tr1/cmath.hpp>namespace boost{ namespace math{namespace detail{//// Implementation of Beta(a,b) using the Lanczos approximation://template <class T, class L, class Policy>T beta_imp(T a, T b, const L&, const Policy& pol){   BOOST_MATH_STD_USING  // for ADL of std names   if(a <= 0)      policies::raise_domain_error<T>("boost::math::beta<%1%>(%1%,%1%)", "The arguments to the beta function must be greater than zero (got a=%1%).", a, pol);   if(b <= 0)      policies::raise_domain_error<T>("boost::math::beta<%1%>(%1%,%1%)", "The arguments to the beta function must be greater than zero (got b=%1%).", b, pol);   T result;   T prefix = 1;   T c = a + b;   // Special cases:   if((c == a) && (b < tools::epsilon<T>()))      return boost::math::tgamma(b, pol);   else if((c == b) && (a < tools::epsilon<T>()))      return boost::math::tgamma(a, pol);   if(b == 1)      return 1/a;   else if(a == 1)      return 1/b;   /*   //   // This code appears to be no longer necessary: it was   // used to offset errors introduced from the Lanczos   // approximation, but the current Lanczos approximations   // are sufficiently accurate for all z that we can ditch   // this.  It remains in the file for future reference...   //   // If a or b are less than 1, shift to greater than 1:   if(a < 1)   {      prefix *= c / a;      c += 1;      a += 1;   }   if(b < 1)   {      prefix *= c / b;      c += 1;      b += 1;   }   */   if(a < b)      std::swap(a, b);   // Lanczos calculation:   T agh = a + L::g() - T(0.5);   T bgh = b + L::g() - T(0.5);   T cgh = c + L::g() - T(0.5);   result = L::lanczos_sum_expG_scaled(a) * L::lanczos_sum_expG_scaled(b) / L::lanczos_sum_expG_scaled(c);   T ambh = a - T(0.5) - b;   if((fabs(b * ambh) < (cgh * 100)) && (a > 100))   {      // Special case where the base of the power term is close to 1      // compute (1+x)^y instead:      result *= exp(ambh * boost::math::log1p(-b / cgh, pol));   }   else   {      result *= pow(agh / cgh, a - T(0.5) - b);   }   if(cgh > 1e10f)      // this avoids possible overflow, but appears to be marginally less accurate:      result *= pow((agh / cgh) * (bgh / cgh), b);   else      result *= pow((agh * bgh) / (cgh * cgh), b);   result *= sqrt(boost::math::constants::e<T>() / bgh);   // If a and b were originally less than 1 we need to scale the result:   result *= prefix;   return result;} // template <class T, class L> beta_imp(T a, T b, const L&)//// Generic implementation of Beta(a,b) without Lanczos approximation support// (Caution this is slow!!!)://template <class T, class Policy>T beta_imp(T a, T b, const lanczos::undefined_lanczos& /* l */, const Policy& pol){   BOOST_MATH_STD_USING   if(a <= 0)      policies::raise_domain_error<T>("boost::math::beta<%1%>(%1%,%1%)", "The arguments to the beta function must be greater than zero (got a=%1%).", a, pol);   if(b <= 0)      policies::raise_domain_error<T>("boost::math::beta<%1%>(%1%,%1%)", "The arguments to the beta function must be greater than zero (got b=%1%).", b, pol);   T result;   T prefix = 1;   T c = a + b;   // special cases:   if((c == a) && (b < tools::epsilon<T>()))      return boost::math::tgamma(b, pol);   else if((c == b) && (a < tools::epsilon<T>()))      return boost::math::tgamma(a, pol);   if(b == 1)      return 1/a;   else if(a == 1)      return 1/b;   // shift to a and b > 1 if required:   if(a < 1)   {      prefix *= c / a;      c += 1;      a += 1;   }   if(b < 1)   {      prefix *= c / b;      c += 1;      b += 1;   }   if(a < b)      std::swap(a, b);   // set integration limits:   T la = (std::max)(T(10), a);   T lb = (std::max)(T(10), b);   T lc = (std::max)(T(10), a+b);   // calculate the fraction parts:   T sa = detail::lower_gamma_series(a, la, pol) / a;   sa += detail::upper_gamma_fraction(a, la, ::boost::math::policies::digits<T, Policy>());   T sb = detail::lower_gamma_series(b, lb, pol) / b;   sb += detail::upper_gamma_fraction(b, lb, ::boost::math::policies::digits<T, Policy>());   T sc = detail::lower_gamma_series(c, lc, pol) / c;   sc += detail::upper_gamma_fraction(c, lc, ::boost::math::policies::digits<T, Policy>());   // and the exponent part:   result = exp(lc - la - lb) * pow(la/lc, a) * pow(lb/lc, b);   // and combine:   result *= sa * sb / sc;   // if a and b were originally less than 1 we need to scale the result:   result *= prefix;   return result;} // template <class T>T beta_imp(T a, T b, const lanczos::undefined_lanczos& l)//// Compute the leading power terms in the incomplete Beta://// (x^a)(y^b)/Beta(a,b) when normalised, and// (x^a)(y^b) otherwise.//// Almost all of the error in the incomplete beta comes from this// function: particularly when a and b are large. Computing large// powers are *hard* though, and using logarithms just leads to// horrendous cancellation errors.//template <class T, class L, class Policy>T ibeta_power_terms(T a,                        T b,                        T x,                        T y,                        const L&,                        bool normalised,                        const Policy& pol){   BOOST_MATH_STD_USING   if(!normalised)   {      // can we do better here?      return pow(x, a) * pow(y, b);   }   T result;   T prefix = 1;   T c = a + b;   // combine power terms with Lanczos approximation:   T agh = a + L::g() - T(0.5);   T bgh = b + L::g() - T(0.5);   T cgh = c + L::g() - T(0.5);   result = L::lanczos_sum_expG_scaled(c) / (L::lanczos_sum_expG_scaled(a) * L::lanczos_sum_expG_scaled(b));   // l1 and l2 are the base of the exponents minus one:   T l1 = (x * b - y * agh) / agh;   T l2 = (y * a - x * bgh) / bgh;   if(((std::min)(fabs(l1), fabs(l2)) < 0.2))   {      // when the base of the exponent is very near 1 we get really      // gross errors unless extra care is taken:      if((l1 * l2 > 0) || ((std::min)(a, b) < 1))      {         //         // This first branch handles the simple cases where either:          //         // * The two power terms both go in the same direction          // (towards zero or towards infinity).  In this case if either          // term overflows or underflows, then the product of the two must          // do so also.           // *Alternatively if one exponent is less than one, then we          // can't productively use it to eliminate overflow or underflow          // from the other term.  Problems with spurious overflow/underflow          // can't be ruled out in this case, but it is *very* unlikely          // since one of the power terms will evaluate to a number close to 1.         //         if(fabs(l1) < 0.1)            result *= exp(a * boost::math::log1p(l1, pol));         else            result *= pow((x * cgh) / agh, a);         if(fabs(l2) < 0.1)            result *= exp(b * boost::math::log1p(l2, pol));         else            result *= pow((y * cgh) / bgh, b);      }      else if((std::max)(fabs(l1), fabs(l2)) < 0.5)      {         //         // Both exponents are near one and both the exponents are          // greater than one and further these two          // power terms tend in opposite directions (one towards zero,          // the other towards infinity), so we have to combine the terms          // to avoid any risk of overflow or underflow.         //         // We do this by moving one power term inside the other, we have:         //         //    (1 + l1)^a * (1 + l2)^b         //  = ((1 + l1)*(1 + l2)^(b/a))^a         //  = (1 + l1 + l3 + l1*l3)^a   ;  l3 = (1 + l2)^(b/a) - 1         //                                    = exp((b/a) * log(1 + l2)) - 1         //         // The tricky bit is deciding which term to move inside :-)         // By preference we move the larger term inside, so that the         // size of the largest exponent is reduced.  However, that can         // only be done as long as l3 (see above) is also small.         //         bool small_a = a < b;         T ratio = b / a;         if((small_a && (ratio * l2 < 0.1)) || (!small_a && (l1 / ratio > 0.1)))         {            T l3 = boost::math::expm1(ratio * boost::math::log1p(l2, pol), pol);            l3 = l1 + l3 + l3 * l1;            l3 = a * boost::math::log1p(l3, pol);            result *= exp(l3);         }         else         {            T l3 = boost::math::expm1(boost::math::log1p(l1, pol) / ratio, pol);            l3 = l2 + l3 + l3 * l2;            l3 = b * boost::math::log1p(l3, pol);            result *= exp(l3);         }      }      else if(fabs(l1) < fabs(l2))      {         // First base near 1 only:         T l = a * boost::math::log1p(l1, pol)            + b * log((y * cgh) / bgh);         result *= exp(l);      }      else      {         // Second base near 1 only:         T l = b * boost::math::log1p(l2, pol)            + a * log((x * cgh) / agh);         result *= exp(l);      }   }   else   {      // general case:      T b1 = (x * cgh) / agh;      T b2 = (y * cgh) / bgh;      l1 = a * log(b1);      l2 = b * log(b2);      if((l1 >= tools::log_max_value<T>())         || (l1 <= tools::log_min_value<T>())         || (l2 >= tools::log_max_value<T>())         || (l2 <= tools::log_min_value<T>())         )      {         // Oops, overflow, sidestep:         if(a < b)            result *= pow(pow(b2, b/a) * b1, a);         else            result *= pow(pow(b1, a/b) * b2, b);      }      else      {         // finally the normal case:         result *= pow(b1, a) * pow(b2, b);      }   }   // combine with the leftover terms from the Lanczos approximation:   result *= sqrt(bgh / boost::math::constants::e<T>());   result *= sqrt(agh / cgh);   result *= prefix;   return result;}//// Compute the leading power terms in the incomplete Beta://// (x^a)(y^b)/Beta(a,b) when normalised, and// (x^a)(y^b) otherwise.//// Almost all of the error in the incomplete beta comes from this// function: particularly when a and b are large. Computing large// powers are *hard* though, and using logarithms just leads to// horrendous cancellation errors.//// This version is generic, slow, and does not use the Lanczos approximation.//template <class T, class Policy>T ibeta_power_terms(T a,                        T b,                        T x,                        T y,                        const boost::math::lanczos::undefined_lanczos&,                        bool normalised,                        const Policy& pol){   BOOST_MATH_STD_USING   if(!normalised)   {      return pow(x, a) * pow(y, b);   }   T result;   T prefix = 1;   T c = a + b;   // integration limits for the gamma functions:   //T la = (std::max)(T(10), a);   //T lb = (std::max)(T(10), b);   //T lc = (std::max)(T(10), a+b);   T la = a + 5;   T lb = b + 5;   T lc = a + b + 5;   // gamma function partials:   T sa = detail::lower_gamma_series(a, la, pol) / a;   sa += detail::upper_gamma_fraction(a, la, ::boost::math::policies::digits<T, Policy>());   T sb = detail::lower_gamma_series(b, lb, pol) / b;   sb += detail::upper_gamma_fraction(b, lb, ::boost::math::policies::digits<T, Policy>());   T sc = detail::lower_gamma_series(c, lc, pol) / c;   sc += detail::upper_gamma_fraction(c, lc, ::boost::math::policies::digits<T, Policy>());   // gamma function powers combined with incomplete beta powers:   T b1 = (x * lc) / la;   T b2 = (y * lc) / lb;   T e1 = lc - la - lb;   T lb1 = a * log(b1);   T lb2 = b * log(b2);   if((lb1 >= tools::log_max_value<T>())      || (lb1 <= tools::log_min_value<T>())      || (lb2 >= tools::log_max_value<T>())      || (lb2 <= tools::log_min_value<T>())      || (e1 >= tools::log_max_value<T>())      || (e1 <= tools::log_min_value<T>())      )   {      result = exp(lb1 + lb2 - e1);   }   else   {      T p1, p2;      if((fabs(b1 - 1) * a < 10) && (a > 1))         p1 = exp(a * boost::math::log1p((x * b - y * la) / la, pol));      else         p1 = pow(b1, a);      if((fabs(b2 - 1) * b < 10) && (b > 1))         p2 = exp(b * boost::math::log1p((y * a - x * lb) / lb, pol));      else         p2 = pow(b2, b);      T p3 = exp(e1);      result = p1 * p2 / p3;   }   // and combine with the remaining gamma function components:   result /= sa * sb / sc;   return result;}//// Series approximation to the incomplete beta://template <class T>struct ibeta_series_t{   typedef T result_type;   ibeta_series_t(T a_, T b_, T x_, T mult) : result(mult), x(x_), apn(a_), poch(1-b_), n(1) {}   T operator()()   {      T r = result / apn;      apn += 1;      result *= poch * x / n;      ++n;      poch += 1;      return r;   }private:   T result, x, apn, poch;   int n;};template <class T, class L, class Policy>T ibeta_series(T a, T b, T x, T s0, const L&, bool normalised, T* p_derivative, T y, const Policy& pol){   BOOST_MATH_STD_USING   T result;   BOOST_ASSERT((p_derivative == 0) || normalised);

⌨️ 快捷键说明

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