Apollo v5.5.0代码解读之Math--Integral

求积方式

I = ∫ a b f ( x ) d x I=\int_{a}^{b}f(x)dx \quad I=abf(x)dx

如果知道被积公式的原函数 F ( x ) F(x) F(x)​,利用牛顿-莱布尼兹公式有:
∫ a b f ( x ) d x = F ( b ) − F ( a ) \int_{a}^{b}f(x)dx=F(b)-F(a) abf(x)dx=F(b)F(a)
此外,还可以使用分割法,分割成无穷多个小区间:
∫ a b f ( x ) d x = lim ⁡ x → ∞ ∑ i = 1 n b − a n f ( a + b − a n i ) \int_{a}^{b}f(x)dx=\lim_{x \to \infty}\sum_{i=1}^{n}\frac{b-a}{n}f(a+\frac{b-a}{n}i) abf(x)dx=xlimi=1nnbaf(a+nbai)
除了以上方式,还可以使用数值积分

integral.h和integral.cc

高斯勒让德积分公式(GaussLegendre)

理论

∫ a b f ( x ) d x ≈ ∑ k = 0 n A k f ( x k ) \int_{a}^{b}f(x)dx\approx \sum_{k=0}^{n}A_kf(x_k) abf(x)dxk=0nAkf(xk)

高斯–勒让德求积公式是构造高精度差值积分的最好方法之一。他是通过让节点和积分系数待定让函数 f ( x ) f(x) f(x)​​​​以此取 i = 0 , 1 , 2... n i=0,1,2 ...n i=0,1,2...n​​​​​次多项式使其尽可能多的能够精确成立来求出积分节点和积分系数。高斯积分的代数精度是 2 n − 1 2n-1 2n1​​​​,而且是最高的。通常的运用的是 ( − 1 , 1 ) (-1,1) (1,1)​​​​的积分节点和积分系数,其他积分域是通过变换公式 x = ( b − a ) ∗ t / 2 + ( b + a ) / 2 x=(b-a)*t/2+(b+a)/2 x=(ba)t/2+(b+a)/2​​​​变换到-1到1之间积分,其中 t ∈ ( − 1 , 1 ) t \in (-1,1) t(1,1)。​
f ( x ) x ∈ ( a , b ) x = ( b − a ) ∗ t / 2 + ( b + a ) / 2 t ∈ ( − 1 , 1 ) ⇔ f ( ( b − a ) ∗ t / 2 + ( b + a ) / 2 ) t ∈ ( − 1 , 1 ) ⇔ g ( t ) t ∈ ( − 1 , 1 ) \begin{aligned} &f(x) \quad x\in(a,b) \\ &x=(b-a)*t/2+(b+a)/2 \quad t\in(-1,1) \\ \Leftrightarrow &f((b-a)*t/2+(b+a)/2) \quad t\in(-1,1) \\ \Leftrightarrow &g(t) \quad t\in(-1,1) \end{aligned} f(x)x(a,b)x=(ba)t/2+(b+a)/2t(1,1)f((ba)t/2+(b+a)/2)t(1,1)g(t)t(1,1)

∫ a b f ( x ) d x = b − a 2 ∫ − 1 1 f ( ( b − a ) ∗ t / 2 + ( b + a ) / 2 ) d t = b − a 2 ∫ − 1 1 g ( t ) d t \int_{a}^{b}f(x)dx= \frac{b-a}{2}\int_{-1}^{1}f((b-a)*t/2+(b+a)/2)dt = \frac{b-a}{2}\int_{-1}^{1}g(t)dt abf(x)dx=2ba11f((ba)t/2+(b+a)/2)dt=2ba11g(t)dt

最终使用的标准形式:
∫ − 1 1 f ( x ) d x ≈ ∑ k = 0 n A k f ( x k ) e . g . n = 4 ∫ − 1 1 f ( x ) d x ≈ A 0 f ( x 0 ) + A 1 f ( x 1 ) + A 2 f ( x 2 ) + A 3 f ( x 3 ) + A 4 f ( x 4 ) 有 五 组 ( n + 1 ) 系 数 ( x k , A k ) \int_{-1}^{1}f(x)dx\approx \sum_{k=0}^{n}A_kf(x_k) \\ e.g. \quad n=4 \\ \int_{-1}^{1}f(x)dx\approx A_0f(x_0) + A_1f(x_1) + A_2f(x_2) + A_3f(x_3) + A_4f(x_4) \\ 有五组(n+1)系数(x_k, A_k) 11f(x)dxk=0nAkf(xk)e.g.n=411f(x)dxA0f(x0)+A1f(x1)+A2f(x2)+A3f(x3)+A4f(x4)(n+1)xk,Ak
下表列出高斯–勒让德求积公式的节点和系数

n x k x_k xk A k A_k Ak
0 0.0 0.0 0.0 2.0 2.0 2.0
1 ± 0.577350269189625764507 \pm 0.577350269189625764507 ±0.577350269189625764507 1.0 1.0 1.0
2 0.0 0.0 0.0
± 0.774596669241483377010 \pm 0.774596669241483377010 ±0.774596669241483377010
0.888888888888888888877 0.888888888888888888877 0.888888888888888888877​​
0.555555555555555555562 0.555555555555555555562 0.555555555555555555562
3 ± 0.339981043584856264792 \pm 0.339981043584856264792 ±0.339981043584856264792
± 0.861136311594052575248 \pm 0.861136311594052575248 ±0.861136311594052575248
0.652145154862546142644 0.652145154862546142644 0.652145154862546142644​​​
0.347854845137453857383 0.347854845137453857383 0.347854845137453857383​​
4 0.0 0.0 0.0
± 0.538469310105683091018 \pm 0.538469310105683091018 ±0.538469310105683091018
± 0.906179845938663992811 \pm 0.906179845938663992811 ±0.906179845938663992811
0.568888888888888888883 0.568888888888888888883 0.568888888888888888883
0.478628670499366468030 0.478628670499366468030 0.478628670499366468030
0.236926885056189087515 0.236926885056189087515 0.236926885056189087515
5 ± 0.661209386466264513688 \pm 0.661209386466264513688 ±0.661209386466264513688
± 0.238619186083196908630 \pm 0.238619186083196908630 ±0.238619186083196908630
± 0.932469514203152027832 \pm 0.932469514203152027832 ±0.932469514203152027832
0.360761573048138607569 0.360761573048138607569 0.360761573048138607569
0.467913934572691047389 0.467913934572691047389 0.467913934572691047389
0.171324492379170345043 0.171324492379170345043 0.171324492379170345043
6 0.0 0.0 0.0
± 0.405845151377397166917 \pm 0.405845151377397166917 ±0.405845151377397166917
± 0.741531185599394439864 \pm 0.741531185599394439864 ±0.741531185599394439864
± 0.949107912342758524541 \pm 0.949107912342758524541 ±0.949107912342758524541
0.417959183673469387749 0.417959183673469387749 0.417959183673469387749
0.381830050505118944961 0.381830050505118944961 0.381830050505118944961
0.279705391489276667890 0.279705391489276667890 0.279705391489276667890
0.129484966168869693274 0.129484966168869693274 0.129484966168869693274
7 ± 0.183434642495649804936 \pm 0.183434642495649804936 ±0.183434642495649804936
± 0.525532409916328985830 \pm 0.525532409916328985830 ±0.525532409916328985830
± 0.796666477413626739567 \pm 0.796666477413626739567 ±0.796666477413626739567
± 0.960289856497536231661 \pm 0.960289856497536231661 ±0.960289856497536231661​​
0.362683783378361982976 0.362683783378361982976 0.362683783378361982976​​​
0.313706645877887287338 0.313706645877887287338 0.313706645877887287338​​​
0.222381034453374470546 0.222381034453374470546 0.222381034453374470546​​​
0.101228536290376259154 0.101228536290376259154 0.101228536290376259154​​​
8 0.0 0.0 0.0
± 0.836031107326635794313 \pm 0.836031107326635794313 ±0.836031107326635794313
± 0.968160239507626089810 \pm 0.968160239507626089810 ±0.968160239507626089810
± 0.324253423403808929042 \pm 0.324253423403808929042 ±0.324253423403808929042
± 0.613371432700590397285 \pm 0.613371432700590397285 ±0.613371432700590397285
0.330239355001259763154 0.330239355001259763154 0.330239355001259763154​​​​
0.180648160694857404059 0.180648160694857404059 0.180648160694857404059​​​​
0.812743883615744119737 0.812743883615744119737 0.812743883615744119737​​​​
0.312347077040002840057 0.312347077040002840057 0.312347077040002840057
0.260610696402935462313 0.260610696402935462313 0.260610696402935462313​​​​
9 ± 0.148874338981631210881 \pm 0.148874338981631210881 ±0.148874338981631210881​​​​​
± 0.433395394129247190794 \pm 0.433395394129247190794 ±0.433395394129247190794​​​​​
± 0.679409568299024406207 \pm 0.679409568299024406207 ±0.679409568299024406207​​​​​
± 0.865063366688984510759 \pm 0.865063366688984510759 ±0.865063366688984510759​​​​​
± 0.973906528517171720066 \pm 0.973906528517171720066 ±0.973906528517171720066​​​​​
0.295524224714752870187 0.295524224714752870187 0.295524224714752870187​​​​
0.269266719309996355105 0.269266719309996355105 0.269266719309996355105​​​​
0.219086362515982044000 0.219086362515982044000 0.219086362515982044000​​​​
0.149451349150580593150 0.149451349150580593150 0.149451349150580593150​​​​
0.666713443086881375920 0.666713443086881375920 0.666713443086881375920​​​​
代码实现
/**
 * @brief Compute the integral of a target single-variable function
 *        from a lower bound to an upper bound, by 5-th Gauss-Legendre method
 * Given a target function and integral lower and upper bound,
 * compute the integral approximation using 5th order Gauss-Legendre
 * integration.
 * The target function must be a smooth function.
 * Example:
 * target function: auto func = [](const double x) {return x * x;};
 *                  double integral = gauss_legendre(func, -2, 3);
 * This gives you the approximated integral of function x^2 in bound [-2, 3]
 *
 * reference: https://en.wikipedia.org/wiki/Gaussian_quadrature
 *            http://www.mymathlib.com/quadrature/gauss_legendre.html
 *
 * @param func The target single-variable function
 * @param lower_bound The lower bound of the integral
 * @param upper_bound The upper bound of the integral
 * @return The integral result
 */
template <std::size_t N>
double IntegrateByGaussLegendre(const std::function<double(double)>& func,
                                const double lower_bound,
                                const double upper_bound) {
  // 根据输入阶数,获取对应系数,这里N对应上表的n+1
  // Apollo提供了N从2到10,也就是上表n从1到9
  auto p = GetGaussLegendrePoints<N>();

  std::array<double, N> x = p.first;
  std::array<double, N> w = p.second;
  // 复化系数
  const double t = (upper_bound - lower_bound) * 0.5;
  const double m = (upper_bound + lower_bound) * 0.5;

  double integral = 0.0;
  for (size_t i = 0; i < N; ++i) {
    // 逐一累加各项和
    integral += w[i] * func(t * x[i] + m);
  }
  // 注意还乘t
  return integral * t;
}

/**
 * @brief Get the points and weights for different ordered Gauss-Legendre
 *        integration. Currently support order 2 - 10. Other input order will
 *        trigger compiling error.
 */
template <std::size_t N>
std::pair<std::array<double, N>, std::array<double, N>>
GetGaussLegendrePoints();

template <>
inline std::pair<std::array<double, 2>, std::array<double, 2>>
GetGaussLegendrePoints<2>() {
  std::array<double, 2> x;
  x[0] = -5.77350269189625764507e-01;
  x[1] = 5.77350269189625764507e-01;

  std::array<double, 2> w;
  w[0] = 1.0;
  w[1] = 1.0;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 3>, std::array<double, 3>>
GetGaussLegendrePoints<3>() {
  std::array<double, 3> x;
  x[0] = 0.00000000000000000000e+00;
  x[1] = 7.74596669241483377010e-01;
  x[2] = -7.74596669241483377010e-01;

  std::array<double, 3> w;
  w[0] = 8.88888888888888888877e-01;
  w[1] = 5.55555555555555555562e-01;
  w[2] = 5.55555555555555555562e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 4>, std::array<double, 4>>
GetGaussLegendrePoints<4>() {
  std::array<double, 4> x;
  x[0] = 3.39981043584856264792e-01;
  x[1] = -3.39981043584856264792e-01;
  x[2] = 8.61136311594052575248e-01;
  x[3] = -8.61136311594052575248e-01;

  std::array<double, 4> w;
  w[0] = 6.52145154862546142644e-01;
  w[1] = 6.52145154862546142644e-01;
  w[2] = 3.47854845137453857383e-01;
  w[3] = 3.47854845137453857383e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 5>, std::array<double, 5>>
GetGaussLegendrePoints<5>() {
  std::array<double, 5> x;
  x[0] = 0.00000000000000000000e+00;
  x[1] = 5.38469310105683091018e-01;
  x[2] = -5.38469310105683091018e-01;
  x[3] = 9.06179845938663992811e-01;
  x[4] = -9.06179845938663992811e-01;

  std::array<double, 5> w;
  w[0] = 5.68888888888888888883e-01;
  w[1] = 4.78628670499366468030e-01;
  w[2] = 4.78628670499366468030e-01;
  w[3] = 2.36926885056189087515e-01;
  w[4] = 2.36926885056189087515e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 6>, std::array<double, 6>>
GetGaussLegendrePoints<6>() {
  std::array<double, 6> x;
  x[0] = 6.61209386466264513688e-01;
  x[1] = -6.61209386466264513688e-01;
  x[2] = 2.38619186083196908630e-01;
  x[3] = -2.38619186083196908630e-01;
  x[4] = 9.32469514203152027832e-01;
  x[5] = -9.32469514203152027832e-01;

  std::array<double, 6> w;
  w[0] = 3.60761573048138607569e-01;
  w[1] = 3.60761573048138607569e-01;
  w[2] = 4.67913934572691047389e-01;
  w[3] = 4.67913934572691047389e-01;
  w[4] = 1.71324492379170345043e-01;
  w[5] = 1.71324492379170345043e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 7>, std::array<double, 7>>
GetGaussLegendrePoints<7>() {
  std::array<double, 7> x;
  x[0] = 0.00000000000000000000e+00;
  x[1] = 4.05845151377397166917e-01;
  x[2] = -4.05845151377397166917e-01;
  x[3] = 7.41531185599394439864e-01;
  x[4] = -7.41531185599394439864e-01;
  x[5] = 9.49107912342758524541e-01;
  x[6] = -9.49107912342758524541e-01;

  std::array<double, 7> w;
  w[0] = 4.17959183673469387749e-01;
  w[1] = 3.81830050505118944961e-01;
  w[2] = 3.81830050505118944961e-01;
  w[3] = 2.79705391489276667890e-01;
  w[4] = 2.79705391489276667890e-01;
  w[5] = 1.29484966168869693274e-01;
  w[6] = 1.29484966168869693274e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 8>, std::array<double, 8>>
GetGaussLegendrePoints<8>() {
  std::array<double, 8> x;
  x[0] = 1.83434642495649804936e-01;
  x[1] = -1.83434642495649804936e-01;
  x[2] = 5.25532409916328985830e-01;
  x[3] = -5.25532409916328985830e-01;
  x[4] = 7.96666477413626739567e-01;
  x[5] = -7.96666477413626739567e-01;
  x[6] = 9.60289856497536231661e-01;
  x[7] = -9.60289856497536231661e-01;

  std::array<double, 8> w;
  w[0] = 3.62683783378361982976e-01;
  w[1] = 3.62683783378361982976e-01;
  w[2] = 3.13706645877887287338e-01;
  w[3] = 3.13706645877887287338e-01;
  w[4] = 2.22381034453374470546e-01;
  w[5] = 2.22381034453374470546e-01;
  w[6] = 1.01228536290376259154e-01;
  w[7] = 1.01228536290376259154e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 9>, std::array<double, 9>>
GetGaussLegendrePoints<9>() {
  std::array<double, 9> x;
  x[0] = 0.00000000000000000000e+00;
  x[1] = 8.36031107326635794313e-01;
  x[2] = -8.36031107326635794313e-01;
  x[3] = 9.68160239507626089810e-01;
  x[4] = -9.68160239507626089810e-01;
  x[5] = 3.24253423403808929042e-01;
  x[6] = -3.24253423403808929042e-01;
  x[7] = 6.13371432700590397285e-01;
  x[8] = -6.13371432700590397285e-01;

  std::array<double, 9> w;
  w[0] = 3.30239355001259763154e-01;
  w[1] = 1.80648160694857404059e-01;
  w[2] = 1.80648160694857404059e-01;
  w[3] = 8.12743883615744119737e-02;
  w[4] = 8.12743883615744119737e-02;
  w[5] = 3.12347077040002840057e-01;
  w[6] = 3.12347077040002840057e-01;
  w[7] = 2.60610696402935462313e-01;
  w[8] = 2.60610696402935462313e-01;

  return std::make_pair(x, w);
}

template <>
inline std::pair<std::array<double, 10>, std::array<double, 10>>
GetGaussLegendrePoints<10>() {
  std::array<double, 10> x;
  x[0] = 1.48874338981631210881e-01;
  x[1] = -1.48874338981631210881e-01;
  x[2] = 4.33395394129247190794e-01;
  x[3] = -4.33395394129247190794e-01;
  x[4] = 6.79409568299024406207e-01;
  x[5] = -6.79409568299024406207e-01;
  x[6] = 8.65063366688984510759e-01;
  x[7] = -8.65063366688984510759e-01;
  x[8] = 9.73906528517171720066e-01;
  x[9] = -9.73906528517171720066e-01;

  std::array<double, 10> w;
  w[0] = 2.95524224714752870187e-01;
  w[1] = 2.95524224714752870187e-01;
  w[2] = 2.69266719309996355105e-01;
  w[3] = 2.69266719309996355105e-01;
  w[4] = 2.19086362515982044000e-01;
  w[5] = 2.19086362515982044000e-01;
  w[6] = 1.49451349150580593150e-01;
  w[7] = 1.49451349150580593150e-01;
  w[8] = 6.66713443086881375920e-02;
  w[9] = 6.66713443086881375920e-02;

  return std::make_pair(x, w);
}

梯形(Trapezoidal)求积公式

理论

要求 f ( x ) f(x) f(x) [ a , b ] [ a , b ] [a,b]上的积分,将积分区间等长分成 n n n段,则每两个分段点之间的距离 h = b − a n h=\frac{b-a}{n} h=nba,然后如下图(图片来源使用 Trapezoidal Rule(梯形法则)求积分
)进行近似:

在这里插入图片描述

则该区间上的积分值就近似等同于每个小梯形的面积之和。

第一个梯形(最左边)的上底 f ( x 0 ) f(x_0) f(x0)​​​,下底 f ( x 1 ) f(x_1) f(x1)​​​,高为 h = b − a n h=\frac{b-a}{n} h=nba​​​,因此对应的面积为 S 1 = ( f ( x 0 ) + f ( x 1 ) ) ∗ h / 2 S_1=(f(x_0)+f(x_1))*h/2 S1=(f(x0)+f(x1))h/2​​​。
以此类推,最后一个(最右边)的上底 f ( x n − 1 ) f(x_{n-1}) f(xn1)​​​,下底 f ( x n ) f(x_n) f(xn)​​​,高为 h = b − a n h=\frac{b-a}{n} h=nba​​​,因此对应的面积为 S n = ( f ( x n − 1 ) + f ( x n ) ) ∗ h / 2 S_n=(f(x_{n-1})+f(x_n))*h/2 Sn=(f(xn1)+f(xn))h/2​​​​。
因此,所有梯形的总面积为:
∫ a b f ( x ) d x ≈ ∑ i = 1 n S i = S 1 + S 2 + . . . + S n = h / 2 [ f ( x 0 ) + f ( x 1 ) + f ( x 1 ) + f ( x 2 ) + . . . + f ( x n − 2 ) + f ( x n − 1 ) + f ( x n − 1 ) + f ( x n ) ] = h 2 [ f ( x 0 ) + 2 ∑ i = 1 n − 1 f ( x i ) + f ( x n ) ] = h ∗ ∑ i = 1 n − 1 f ( x i ) + 0.5 ∗ h ∗ ( f ( x 0 ) + f ( x n ) ) \begin{aligned} \int_{a}^{b}f(x)dx & \approx \sum_{i=1}^{n}S_i = S_1+S_2+...+S_n \\ &= h/2[f(x_0)+f(x_1)+f(x_1)+f(x_2)+...+f(x_{n-2})+f(x_{n-1})+f(x_{n-1})+f(x_n)] \\ &= \frac{h}{2}[f(x_0)+2\sum_{i=1}^{n-1}f(x_i)+f(x_n)] \\ &= h*\sum_{i=1}^{n-1}f(x_i)+0.5*h*(f(x_0)+f(x_n)) \end{aligned} abf(x)dxi=1nSi=S1+S2+...+Sn=h/2[f(x0)+f(x1)+f(x1)+f(x2)+...+f(xn2)+f(xn1)+f(xn1)+f(xn)]=2h[f(x0)+2i=1n1f(xi)+f(xn)]=hi=1n1f(xi)+0.5h(f(x0)+f(xn))

代码实现
double IntegrateByTrapezoidal(const std::vector<double>& func, const double dx,
                              const std::size_t nsteps) {
  // nsteps表示func集合元素的个数,也就是下标从0到nsteps-1,也就是意味着有nsteps-1个梯形
  // 如参func为f(x_0)到f(x_{nsteps-1})的集合,有nsteps个数,有nsteps-1个梯形
  // dx就是步长(b-a)/(nsteps-1),也就是公式里面的h
  double sum = 0;
  for (std::size_t i = 1; i + 1 < nsteps; ++i) {
    // 从i=1到i=nsteps-2的累加和
    sum += func[i];
  }
  return dx * sum + 0.5 * dx * (func[0] + func[nsteps - 1]);
}

辛普森(Simpson)求积公式

理论

要求将区间划分成偶数个。图片来自辛普森积分法

在这里插入图片描述
辛普森积分法是一种用抛物线近似函数曲线来求定积分数值解的方法。把积分区间等分成若干段,对被积函数在每一段上使用辛普森公式,根据其在每一段的两端和中点处的值近似为抛物线,逐段积分后加起来,即得到原定积分的数值解。

基本思想就是把复杂的函数 f ( x ) f(x) f(x)​​近似成二次函数。

∫ a b f ( x ) d x ≈ ∫ a b ( A x 2 + B x + C ) d x = A 3 ( b 3 − a 3 ) + B 2 ( b 2 − a 2 ) + C ( b − a ) = 2 A ( b 3 − a 3 ) + 3 B ( b 2 − a 2 ) + 6 C ( b − a ) 6 = ( b − a ) [ 2 A ( b 2 + a b + a 2 ) + 3 B ( b + a ) + 6 C ] 6 = ( b − a ) ( A a 2 + B a + C + A b 2 + B b + C + A a 2 + 2 A a b + A b 2 + 2 B b + 2 B a + 4 C ) 6 = ( b − a ) [ f ( a ) + f ( b ) + A ( a + b ) 2 + 2 B ( a + b ) + 4 C ] 6 = ( b − a ) [ f ( a ) + f ( b ) + 4 ( A ( a + b 2 ) 2 + B a + b 2 + C ) ] 6 = ( b − a ) [ f ( a ) + f ( b ) + 4 f ( a + b 2 ) ] 6 \begin{aligned} \int_a^bf(x)dx &\approx \int_a^b(Ax^2+Bx+C)dx \\ &=\frac{A}{3}(b^3-a^3)+\frac{B}{2}(b^2-a^2)+C(b-a) \\ &=\frac{2A(b^3-a^3)+3B(b^2-a^2)+6C(b-a)}{6} \\ &=\frac{(b-a)[2A(b^2+ab+a^2)+3B(b+a)+6C]}{6} \\ &=\frac{(b-a)(Aa^2+Ba+C+Ab^2+Bb+C+Aa^2+2Aab+Ab^2+2Bb+2Ba+4C)}{6} \\ &=\frac{(b-a)[f(a)+f(b)+A(a+b)^2+2B(a+b)+4C]}{6} \\ &=\frac{(b-a)[f(a)+f(b)+4(A(\frac{a+b}{2})^2+B\frac{a+b}{2}+C)]}{6} \\ &=\frac{(b-a)[f(a)+f(b)+4f(\frac{a+b}{2})]}{6} \end{aligned} abf(x)dxab(Ax2+Bx+C)dx=3A(b3a3)+2B(b2a2)+C(ba)=62A(b3a3)+3B(b2a2)+6C(ba)=6(ba)[2A(b2+ab+a2)+3B(b+a)+6C]=6(ba)(Aa2+Ba+C+Ab2+Bb+C+Aa2+2Aab+Ab2+2Bb+2Ba+4C)=6(ba)[f(a)+f(b)+A(a+b)2+2B(a+b)+4C]=6(ba)[f(a)+f(b)+4(A(2a+b)2+B2a+b+C)]=6(ba)[f(a)+f(b)+4f(2a+b)]
先求定积分 ∫ a b f ( x ) d x ( a < b ) \int_a^bf(x)dx \quad (a<b) abf(x)dx(a<b)

将闭区间等分成 2 n 2n 2n​​​,即偶数个小区间 [ x i , x i + 1 ] ( x i < x i + 1 , x 0 = a , x 2 n = b , i ∈ [ 0 , 2 n ] ) [x_i, x_{i+1}] \quad (x_i<x_{i+1},x_0=a,x_{2n}=b,i\in[0,2n]) [xi,xi+1](xi<xi+1,x0=a,x2n=b,i[0,2n])​​​​。在每个小区间上,用抛物线近似函数 f ( x ) f(x) f(x)​​的曲线。每个区间长度为 b − a 2 n \frac{b-a}{2n} 2nba​。

两个区间为基本单位,基本单位长度为 b − a n \frac{b-a}{n} nba,使用辛普森公式近似求积分:
e . g . ∫ x 0 x 2 f ( x ) d x ≈ ( x 2 − x 0 ) [ f ( x 0 ) + f ( x 2 ) + 4 f ( x 1 ) ] 6 = ( b − a ) 6 n [ f ( x 0 ) + 4 f ( x 1 ) + f ( x 2 ) ] \begin{aligned} e.g. \quad \int_{x_0}^{x_2} f(x)dx & \approx \frac{(x_2-x_0)[f(x_0)+f(x_2)+4f(x_1)]}{6} \\ & = \frac{(b-a)}{6n} [f(x_0)+4f(x_1)+f(x_2)]\\ \end{aligned} e.g.x0x2f(x)dx6(x2x0)[f(x0)+f(x2)+4f(x1)]=6n(ba)[f(x0)+4f(x1)+f(x2)]

e . g . ∫ x 2 n − 2 x 2 n f ( x ) d x ≈ ( x 2 n − x 2 n − 2 ) [ f ( x 2 n − 2 ) + f ( x 2 n ) + 4 f ( x 2 n − 1 ) ] 6 = ( b − a ) 6 n [ f ( x 2 n − 2 ) + 4 f ( x 2 n − 1 ) + f ( x 2 n ) ] \begin{aligned} e.g. \quad \int_{x_{2n-2}}^{x_{2n}} f(x)dx & \approx \frac{(x_{2n}-x_{2n-2})[f(x_{2n-2})+f(x_{2n})+4f(x_{2n-1})]}{6} \\ & = \frac{(b-a)}{6n} [f(x_{2n-2})+4f(x_{2n-1})+f(x_{2n})]\\ \end{aligned} e.g.x2n2x2nf(x)dx6(x2nx2n2)[f(x2n2)+f(x2n)+4f(x2n1)]=6n(ba)[f(x2n2)+4f(x2n1)+f(x2n)]

所以累加全部基本单位,可得:
∫ a b f ( x ) d x ≈ ∫ x 0 x 2 f ( x ) d x + ∫ x 2 x 4 f ( x ) d x + . . . + ∫ x 2 n − 2 x 2 n f ( x ) d x = b − a 6 n [ f ( x 0 ) + 4 f ( x 1 ) + f ( x 2 ) + f ( x 2 ) + 4 f ( x 3 ) + f ( x 4 ) + . . . + f ( x 2 n − 2 ) + 4 f ( x 2 n − 1 ) + f ( x 2 n ) ] = b − a 3 ∗ 2 n [ f ( x 0 ) + f ( x 2 n ) + 4 [ f ( x 1 ) + f ( x 3 ) + . . . + f ( x 2 n − 1 ) ] + 2 [ f ( x 2 ) + f ( x 4 ) + . . . + f ( x 2 n − 2 ) ] ] \begin{aligned} \int_a^bf(x)dx & \approx \int_{x_0}^{x_2} f(x)dx+\int_{x_2}^{x_4} f(x)dx+...+\int_{x_{2n-2}}^{x_{2n}} f(x)dx \\ & =\frac{b-a}{6n}[f(x_0)+4f(x_1)+f(x_2) + f(x_2)+4f(x_3)+f(x_4) + ... + f(x_{2n-2})+4f(x_{2n-1})+f(x_{2n})] \\ & =\frac{b-a}{3*2n}[f(x_0)+f(x_{2n}) + 4[f(x_1)+f(x_3)+...+f(x_{2n-1})] + 2[f(x_2)+f(x_4)+...+f(x_{2n-2})]] \\ \end{aligned} abf(x)dxx0x2f(x)dx+x2x4f(x)dx+...+x2n2x2nf(x)dx=6nba[f(x0)+4f(x1)+f(x2)+f(x2)+4f(x3)+f(x4)+...+f(x2n2)+4f(x2n1)+f(x2n)]=32nba[f(x0)+f(x2n)+4[f(x1)+f(x3)+...+f(x2n1)]+2[f(x2)+f(x4)+...+f(x2n2)]]

代码实现
double IntegrateBySimpson(const std::vector<double>& func, const double dx,
                          const std::size_t nsteps) {
  // nsteps表示func集合元素的个数,也就是下标从0到nsteps-1,也就是意味着有nsteps-1个梯形
  // 要求nsteps为奇数,也就是平分成偶数个梯形,nsteps-1个区间
  // 如参func为f(x_0)到f(x_{nsteps-1})的集合,有nsteps个数,有nsteps-1个梯形,即偶数个梯形
  // dx就是步长(b-a)/(nsteps-1),也就是每个区间的长度
  CHECK_EQ(1, nsteps & 1);
  double sum1 = 0.0;
  double sum2 = 0.0;
  for (std::size_t i = 1; i + 1 < nsteps; ++i) {
    if ((i & 1) != 0) {
      // 奇数求和
      sum1 += func[i];
    } else {
      // 偶数求和
      sum2 += func[i];
    }
  }
  return dx / 3.0 * (4.0 * sum1 + 2.0 * sum2 + func[0] + func[nsteps - 1]);
}

参考文献

使用 Trapezoidal Rule(梯形法则)求积分

自适应辛普森(Simpson)积分

辛普森积分法

数值分析高斯—勒让德积分公式

高斯-勒让德积分公式

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值