|
| 1 | +/** |
| 2 | + * @file asr.cpp |
| 3 | + * @brief |
| 4 | + * @author Haoming Bai <haomingbai@hotmail.com> |
| 5 | + * @date 2025-08-20 |
| 6 | + * |
| 7 | + * Copyright © 2025 Haoming Bai |
| 8 | + * SPDX-License-Identifier: MIT |
| 9 | + * |
| 10 | + * @details |
| 11 | + */ |
| 12 | + |
| 13 | +#include <cmath> |
| 14 | +#include <concepts> |
| 15 | +#include <cstdlib> |
| 16 | + |
| 17 | +/** |
| 18 | + * @brief Simpson's Rule for numerical integration. |
| 19 | + * |
| 20 | + * Simpson's Rule is a method for approximating the definite integral of a |
| 21 | + * function by fitting a quadratic polynomial to the function at three points |
| 22 | + * (the endpoints and the midpoint) and integrating that polynomial over the |
| 23 | + * interval [a, b]. The formula for the approximation is: (b - a)/6 * [f(a) + |
| 24 | + * 4f((a+b)/2) + f(b)]. |
| 25 | + * |
| 26 | + * This method provides exact results for polynomials up to degree 3 and |
| 27 | + * generally offers better accuracy than the trapezoidal rule for smooth |
| 28 | + * functions. |
| 29 | + * |
| 30 | + * @tparam Func Callable type. Must be invocable with a floating-point argument |
| 31 | + * and return a value convertible to a floating-point type. |
| 32 | + * @tparam F1 Floating-point type for the lower bound. |
| 33 | + * @tparam F2 Floating-point type for the upper bound. |
| 34 | + * |
| 35 | + * @param f The function to be integrated. Should be continuous on [a, b]. |
| 36 | + * @param a Lower limit of integration. |
| 37 | + * @param b Upper limit of integration. |
| 38 | + * |
| 39 | + * @return Approximation of the integral ∫_{a}^{b} f(x) dx. |
| 40 | + * The return type is deduced as the common type of F1 and F2. |
| 41 | + * |
| 42 | + * @note This is a non-adaptive implementation that provides a single |
| 43 | + * approximation over the entire interval. For higher accuracy over complex |
| 44 | + * functions, consider using an adaptive method like Adaptive Simpson's Rule |
| 45 | + * (ASR). |
| 46 | + * @note Requires that `f` be defined and continuous on [a, b]. |
| 47 | + * |
| 48 | + * Example usage: |
| 49 | + * @code{.cpp} |
| 50 | + * auto result = Simpson([](double x) { return std::sin(x); }, 0.0, 3.14159); |
| 51 | + * @endcode |
| 52 | + */ |
| 53 | +template <typename Func, std::floating_point F1, std::floating_point F2> |
| 54 | +auto Simpson(Func f, F1 a, F2 b) |
| 55 | + -> decltype(std::declval<F1>() + std::declval<F2>()) { |
| 56 | + using ReturnType = decltype(std::declval<F1>() + std::declval<F2>()); |
| 57 | + |
| 58 | + ReturnType mid = a + (b - a) / 2.0; |
| 59 | + ReturnType result = f(a) + f(b) + f(mid) * 4.0; |
| 60 | + result *= (b - a); |
| 61 | + result /= 6.0; |
| 62 | + |
| 63 | + return result; |
| 64 | +} |
| 65 | + |
| 66 | +/** |
| 67 | + * @brief Adaptive Simpson's Rule (ASR) for numerical integration. |
| 68 | + * |
| 69 | + * Adaptive Simpson's Rule is a recursive method for approximating the definite |
| 70 | + * integral of a function. It dynamically refines the integration interval by |
| 71 | + * subdividing it into smaller segments where the function exhibits more |
| 72 | + * variation, and uses coarse segments where the function is smooth. This allows |
| 73 | + * for efficient computation by minimizing the number of function evaluations |
| 74 | + * while achieving the desired accuracy. |
| 75 | + * |
| 76 | + * The method compares the Simpson's rule estimates on the current segment and |
| 77 | + * its two halves. If the difference between the composite estimate and the |
| 78 | + * whole segment estimate is within the error tolerance, the result is accepted. |
| 79 | + * Otherwise, the algorithm recurses on both halves. |
| 80 | + * |
| 81 | + * @tparam Func Callable type. Must be invocable with a floating-point argument |
| 82 | + * and return a value convertible to a floating-point type. |
| 83 | + * @tparam F1 Floating-point type for the lower bound. |
| 84 | + * @tparam F2 Floating-point type for the upper bound. |
| 85 | + * @tparam Ferr Floating-point type for the error tolerance. |
| 86 | + * |
| 87 | + * @param f The function to be integrated. Must be continuous on [a, b]. |
| 88 | + * @param a Lower limit of integration. |
| 89 | + * @param b Upper limit of integration. |
| 90 | + * @param error Maximum allowable error tolerance for the integral |
| 91 | + * approximation. |
| 92 | + * |
| 93 | + * @return Approximation of the integral ∫_{a}^{b} f(x) dx. |
| 94 | + * The return type is deduced as the common type of F1 and F2. |
| 95 | + * |
| 96 | + * @note The function uses recursion and may cause stack overflow for extremely |
| 97 | + * tight tolerances or highly oscillatory functions over large intervals. |
| 98 | + * @note Requires that `f` be defined and continuous on [a, b]. |
| 99 | + * |
| 100 | + * Example usage: |
| 101 | + * @code{.cpp} |
| 102 | + * auto result = ASR([](double x) { return std::sin(x); }, 0.0, 3.14159, 1e-6); |
| 103 | + * @endcode |
| 104 | + */ |
| 105 | +template <typename Func, std::floating_point F1, std::floating_point F2, |
| 106 | + std::floating_point Ferr> |
| 107 | +auto ASR(Func f, F1 a, F2 b, Ferr error) |
| 108 | + -> decltype(std::declval<F1>() + std::declval<F2>()) { |
| 109 | + using ReturnType = decltype(std::declval<F1>() + std::declval<F2>()); |
| 110 | + ReturnType mid = a + (b - a) / 2.0; |
| 111 | + |
| 112 | + // 先对整体使用辛普森法 |
| 113 | + auto S0 = Simpson(f, a, b); |
| 114 | + // 再将区间分成两块求解 |
| 115 | + auto S1 = Simpson(f, a, mid); |
| 116 | + auto S2 = Simpson(f, mid, b); |
| 117 | + |
| 118 | + // 求解此时的误差 |
| 119 | + Ferr curr_error = std::abs(S0 - (S1 + S2)); |
| 120 | + |
| 121 | + // 误差如果超过最大允许范围, |
| 122 | + // (因为在返回过程中, 差值会被除以15, |
| 123 | + // 所以curr_error是15倍的当前误差) |
| 124 | + // 那么就递归求解两侧的面积, |
| 125 | + // 同时要求两侧的误差不超过最大误差的一半. |
| 126 | + // 否则就直接返回结果. |
| 127 | + if (curr_error >= 15.0 * error) { |
| 128 | + ReturnType result = |
| 129 | + ASR(f, a, mid, error / 2.0) + ASR(f, mid, b, error / 2.0); |
| 130 | + return result; |
| 131 | + } else { |
| 132 | + ReturnType result = (S1 + S2) + (S1 + S2 - S0) / 15.0; |
| 133 | + return result; |
| 134 | + } |
| 135 | +} |
0 commit comments