ibeta_inverse.hpp
来自「Boost provides free peer-reviewed portab」· HPP 代码 · 共 940 行 · 第 1/3 页
HPP
940 行
T temme_method_3_ibeta_inverse(T a, T b, T p, T q, const Policy& pol){ BOOST_MATH_STD_USING // ADL of std names // // Begin by getting an initial approximation for the quantity // eta from the dominant part of the incomplete beta: // T eta0; if(p < q) eta0 = boost::math::gamma_q_inv(b, p, pol); else eta0 = boost::math::gamma_p_inv(b, q, pol); eta0 /= a; // // Define the variables and powers we'll need later on: // T mu = b / a; T w = sqrt(1 + mu); T w_2 = w * w; T w_3 = w_2 * w; T w_4 = w_2 * w_2; T w_5 = w_3 * w_2; T w_6 = w_3 * w_3; T w_7 = w_4 * w_3; T w_8 = w_4 * w_4; T w_9 = w_5 * w_4; T w_10 = w_5 * w_5; T d = eta0 - mu; T d_2 = d * d; T d_3 = d_2 * d; T d_4 = d_2 * d_2; T w1 = w + 1; T w1_2 = w1 * w1; T w1_3 = w1 * w1_2; T w1_4 = w1_2 * w1_2; // // Now we need to compute the purturbation error terms that // convert eta0 to eta, these are all polynomials of polynomials. // Probably these should be re-written to use tabulated data // (see examples above), but it's less of a win in this case as we // need to calculate the individual powers for the denominator terms // anyway, so we might as well use them for the numerator-polynomials // as well.... // // Refer to p154-p155 for the details of these expansions: // T e1 = (w + 2) * (w - 1) / (3 * w); e1 += (w_3 + 9 * w_2 + 21 * w + 5) * d / (36 * w_2 * w1); e1 -= (w_4 - 13 * w_3 + 69 * w_2 + 167 * w + 46) * d_2 / (1620 * w1_2 * w_3); e1 -= (7 * w_5 + 21 * w_4 + 70 * w_3 + 26 * w_2 - 93 * w - 31) * d_3 / (6480 * w1_3 * w_4); e1 -= (75 * w_6 + 202 * w_5 + 188 * w_4 - 888 * w_3 - 1345 * w_2 + 118 * w + 138) * d_4 / (272160 * w1_4 * w_5); T e2 = (28 * w_4 + 131 * w_3 + 402 * w_2 + 581 * w + 208) * (w - 1) / (1620 * w1 * w_3); e2 -= (35 * w_6 - 154 * w_5 - 623 * w_4 - 1636 * w_3 - 3983 * w_2 - 3514 * w - 925) * d / (12960 * w1_2 * w_4); e2 -= (2132 * w_7 + 7915 * w_6 + 16821 * w_5 + 35066 * w_4 + 87490 * w_3 + 141183 * w_2 + 95993 * w + 21640) * d_2 / (816480 * w_5 * w1_3); e2 -= (11053 * w_8 + 53308 * w_7 + 117010 * w_6 + 163924 * w_5 + 116188 * w_4 - 258428 * w_3 - 677042 * w_2 - 481940 * w - 105497) * d_3 / (14696640 * w1_4 * w_6); T e3 = -((3592 * w_7 + 8375 * w_6 - 1323 * w_5 - 29198 * w_4 - 89578 * w_3 - 154413 * w_2 - 116063 * w - 29632) * (w - 1)) / (816480 * w_5 * w1_2); e3 -= (442043 * w_9 + 2054169 * w_8 + 3803094 * w_7 + 3470754 * w_6 + 2141568 * w_5 - 2393568 * w_4 - 19904934 * w_3 - 34714674 * w_2 - 23128299 * w - 5253353) * d / (146966400 * w_6 * w1_3); e3 -= (116932 * w_10 + 819281 * w_9 + 2378172 * w_8 + 4341330 * w_7 + 6806004 * w_6 + 10622748 * w_5 + 18739500 * w_4 + 30651894 * w_3 + 30869976 * w_2 + 15431867 * w + 2919016) * d_2 / (146966400 * w1_4 * w_7); // // Combine eta0 and the error terms to compute eta (Second eqaution p155): // T eta = eta0 + e1 / a + e2 / (a * a) + e3 / (a * a * a); // // Now we need to solve Eq 4.2 to obtain x. For any given value of // eta there are two solutions to this equation, and since the distribtion // may be very skewed, these are not related by x ~ 1-x we used when // implementing section 3 above. However we know that: // // cross < x <= 1 ; iff eta < mu // x == cross ; iff eta == mu // 0 <= x < cross ; iff eta > mu // // Where cross == 1 / (1 + mu) // Many thanks to Prof Temme for clarifying this point. // // Therefore we'll just jump straight into Newton iterations // to solve Eq 4.2 using these bounds, and simple bisection // as the first guess, in practice this converges pretty quickly // and we only need a few digits correct anyway: // if(eta <= 0) eta = tools::min_value<T>(); T u = eta - mu * log(eta) + (1 + mu) * log(1 + mu) - mu; T cross = 1 / (1 + mu); T lower = eta < mu ? cross : 0; T upper = eta < mu ? 1 : cross; T x = (lower + upper) / 2; x = tools::newton_raphson_iterate( temme_root_finder<T>(u, mu), x, lower, upper, policies::digits<T, Policy>() / 2);#ifdef BOOST_INSTRUMENT std::cout << "Estimating x with Temme method 3: " << x << std::endl;#endif return x;}template <class T, class Policy>struct ibeta_roots{ ibeta_roots(T _a, T _b, T t, bool inv = false) : a(_a), b(_b), target(t), invert(inv) {} std::tr1::tuple<T, T, T> operator()(T x) { BOOST_MATH_STD_USING // ADL of std names BOOST_FPU_EXCEPTION_GUARD T f1; T y = 1 - x; T f = ibeta_imp(a, b, x, Policy(), invert, true, &f1) - target; if(invert) f1 = -f1; if(y == 0) y = tools::min_value<T>() * 64; if(x == 0) x = tools::min_value<T>() * 64; T f2 = f1 * (-y * a + (b - 2) * x + 1); if(fabs(f2) < y * x * tools::max_value<T>()) f2 /= (y * x); if(invert) f2 = -f2; // make sure we don't have a zero derivative: if(f1 == 0) f1 = (invert ? -1 : 1) * tools::min_value<T>() * 64; return std::tr1::make_tuple(f, f1, f2); }private: T a, b, target; bool invert;};template <class T, class Policy>T ibeta_inv_imp(T a, T b, T p, T q, const Policy& pol, T* py){ BOOST_MATH_STD_USING // For ADL of math functions. // // The flag invert is set to true if we swap a for b and p for q, // in which case the result has to be subtracted from 1: // bool invert = false; // // Depending upon which approximation method we use, we may end up // calculating either x or y initially (where y = 1-x): // T x = 0; // Set to a safe zero to avoid a // MSVC 2005 warning C4701: potentially uninitialized local variable 'x' used // But code inspection appears to ensure that x IS assigned whatever the code path. T y; // For some of the methods we can put tighter bounds // on the result than simply [0,1]: // T lower = 0; T upper = 1; // // Student's T with b = 0.5 gets handled as a special case, swap // around if the arguments are in the "wrong" order: // if(a == 0.5f) { std::swap(a, b); std::swap(p, q); invert = !invert; } // // Handle trivial cases first: // if(q == 0) { if(py) *py = 0; return 1; } else if(p == 0) { if(py) *py = 1; return 0; } else if((a == 1) && (b == 1)) { if(py) *py = 1 - p; return p; } else if((b == 0.5f) && (a >= 0.5f)) { // // We have a Student's T distribution: x = find_ibeta_inv_from_t_dist(a, p, q, &y, pol); } else if(a + b > 5) { // // When a+b is large then we can use one of Prof Temme's // asymptotic expansions, begin by swapping things around // so that p < 0.5, we do this to avoid cancellations errors // when p is large. // if(p > 0.5) { std::swap(a, b); std::swap(p, q); invert = !invert; } T minv = (std::min)(a, b); T maxv = (std::max)(a, b); if((sqrt(minv) > (maxv - minv)) && (minv > 5)) { // // When a and b differ by a small amount // the curve is quite symmetrical and we can use an error // function to approximate the inverse. This is the cheapest // of the three Temme expantions, and the calculated value // for x will never be much larger than p, so we don't have // to worry about cancellation as long as p is small. // x = temme_method_1_ibeta_inverse(a, b, p, pol); y = 1 - x; } else { T r = a + b; T theta = asin(sqrt(a / r)); T lambda = minv / r; if((lambda >= 0.2) && (lambda <= 0.8) && (lambda >= 10)) { // // The second error function case is the next cheapest // to use, it brakes down when the result is likely to be // very small, if a+b is also small, but we can use a // cheaper expansion there in any case. As before x won't // be much larger than p, so as long as p is small we should // be free of cancellation error. // T ppa = pow(p, 1/a); if((ppa < 0.0025) && (a + b < 200)) { x = ppa * pow(a * boost::math::beta(a, b, pol), 1/a); } else x = temme_method_2_ibeta_inverse(a, b, p, r, theta, pol); y = 1 - x; } else { // // If we get here then a and b are very different in magnitude // and we need to use the third of Temme's methods which // involves inverting the incomplete gamma. This is much more // expensive than the other methods. We also can only use this // method when a > b, which can lead to cancellation errors // if we really want y (as we will when x is close to 1), so // a different expansion is used in that case. // if(a < b) { std::swap(a, b); std::swap(p, q); invert = !invert; } // // Try and compute the easy way first: // T bet = 0; if(b < 2) bet = boost::math::beta(a, b, pol); if(bet != 0) { y = pow(b * q * bet, 1/b); x = 1 - y; } else y = 1; if(y > 1e-5) { x = temme_method_3_ibeta_inverse(a, b, p, q, pol); y = 1 - x; } } } } else if((a < 1) && (b < 1)) { // // Both a and b less than 1, // there is a point of inflection at xs: // T xs = (1 - a) / (2 - a - b); // // Now we need to ensure that we start our iteration from the // right side of the inflection point: // T fs = boost::math::ibeta(a, b, xs, pol) - p; if(fabs(fs) / p < tools::epsilon<T>() * 3) { // The result is at the point of inflection, best just return it: *py = invert ? xs : 1 - xs; return invert ? 1-xs : xs; } if(fs < 0) { std::swap(a, b); std::swap(p, q); invert = true; xs = 1 - xs; } T xg = pow(a * p * boost::math::beta(a, b, pol), 1/a); x = xg / (1 + xg); y = 1 / (1 + xg);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?