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 + -
显示快捷键?