Romberg求积法(C++)

Romberg求积法是一种数值分析中的求积算法, 它利用了外推的思想方法, 以提高代数精度. 这种方法通过不断缩小积分区间, 将区间划分为更小的子区间, 并计算每个子区间的积分值, 从而逼近更为准确的求积值. 这个过程是通过计算一系列的近似值, 并逐步改进这些近似值, 以得到更为精确的结果. 在实现过程中, 通常采用逐次分半加速法, 将积分区间逐次分割为更小的子区间. 前一次分割得到的函数值在分半之后仍可被利用, 且易于编程实现.

算法说明

首先我们有复化梯形公式的余项:
I − T ( h ) = ∑ k = 1 m B 2 k ( 2 k ) ! [ f ( 2 k − 1 ) ( a ) − f ( 2 k − 1 ) ( b ) ] h 2 k + r m + 1 I-T(h)=\sum_{k=1}^m\frac{B_{2k}}{(2k)!}[f^{(2k-1)}(a)-f^{(2k-1)}(b)]h^{2k}+r_{m+1} IT(h)=k=1m(2k)!B2k[f(2k1)(a)f(2k1)(b)]h2k+rm+1

其中, B 2 k B_{2k} B2k为Bernoulli常数,
r m + 1 : = − B 2 m + 2 ( 2 m + 2 ) ! ( b − a ) f ( 2 m + 2 ) ( η ) h 2 m + 2 , η ∈ ( a , b ) r_{m+1}:=-\frac{B_{2m+2}}{\left(2m+2\right)!}(b-a)f^{\left(2m+2\right)}\left(\eta\right)h^{2m+2},\quad\eta\in(a,b) rm+1:=(2m+2)!B2m+2(ba)f(2m+2)(η)h2m+2,η(a,b)

此外, 我们还有Richardson外推法如下:
假设 F ( h ) F(h) F(h)逼近 F ( 0 ) F(0) F(0)的余项为
F ( h ) − F ( 0 ) = a 1 h p 1 + a 2 h p 2 + ⋯ F(h)-F(0)=a_1h^{p_1}+a_2h^{p_2}+\cdots F(h)F(0)=a1hp1+a2hp2+
其中 p 1 < p 2 < ⋯ p_1<p_2<\cdots p1<p2<, a k a_k ak是与 h h h无关的非零常数, 则由
{ F 0 ( h ) = F ( h ) F k + 1 ( h ) = q p k F k ( h / q ) − F k ( h ) q p k − 1 , k = 0 , 1 , 2 , ⋯ \begin{cases} F_0(h)=F(h)\\ F_{k+1}(h)=\dfrac{q^{p_k}F_k(h/q)-F_k(h)}{q^{p_k}-1},k=0,1,2,\cdots \end{cases} F0(h)=F(h)Fk+1(h)=qpk1qpkFk(h/q)Fk(h),k=0,1,2,
定义的序列 { F k ( h ) } \{F_k(h)\} {Fk(h)}
F n ( h ) − F ( 0 ) = a n + 1 ( n ) h p n + 1 + a n + 2 ( n ) h p n + 2 + a n + 3 ( n ) h p n + 3 + ⋯ F_n(h)-F(0)=a_{n+1}^{(n)}h^{p_{n+1}}+a_{n+2}^{(n)}h^{p_{n+2}}+a_{n+3}^{(n)}h^{p_{n+3}}+\cdots Fn(h)F(0)=an+1(n)hpn+1+an+2(n)hpn+2+an+3(n)hpn+3+
其中, a n + k ( n ) ( k = 1 , 2 , ⋯   ) a_{n+k}^{(n)}(k=1,2,\cdots) an+k(n)(k=1,2,) h h h无关, q > 1 q>1 q>1.

利用复化梯形余项公式和Richardson外推法可得Romberg求积法:
{ T 1 ( 0 ) = b − a 2 [ f ( a ) + f ( b ) ] T 1 ( i ) = 1 2 T 1 ( i − 1 ) + b − a 2 i ∑ j = 1 2 i − 1 f [ a + 2 j − 1 2 i ( b − a ) ] , i = 1 , 2 , ⋯ T m + 1 ( k − 1 ) = 4 m T m ( k ) − T m ( k − 1 ) 4 m − 1 , k = 1 , 2 , ⋯   , i , m = 1 , 2 , ⋯ \begin{cases} T_{1}^{(0)}=\dfrac{b-a}{2}\big[f(a)+f(b)\big]\\ \displaystyle T_{1}^{(i)}=\frac{1}{2}T_{1}^{(i-1)}+\frac{b-a}{2^{i}}\sum_{j=1}^{2^{i-1}}f\left[a+\frac{2j-1}{2^{i}}(b-a)\right],i=1,2,\cdots\\ \displaystyle T_{m+1}^{(k-1)}=\frac{4^{m}T_{m}^{(k)}-T_{m}^{(k-1)}}{4^{m}-1},k=1,2,\cdots,i,m=1,2,\cdots \end{cases} T1(0)=2ba[f(a)+f(b)]T1(i)=21T1(i1)+2ibaj=12i1f[a+2i2j1(ba)],i=1,2,Tm+1(k1)=4m14mTm(k)Tm(k1),k=1,2,,i,m=1,2,

对给定的精度标准 ε \varepsilon ε, 我们可由
∣ T m ( 0 ) − T m − 1 ( 0 ) ∣ < ε |T^{(0)}_m-T^{(0)}_{m-1}|<\varepsilon Tm(0)Tm1(0)<ε
作为迭代终止的标准.

算法实现

首先包含头文件如下:

#include <functional>
#include <math.h>
using namespace std;
typedef function<double(double)> func;

接着定义一个“T”表类:

class Romberg_Table
{
    double *arr;
    unsigned n, k;

public:
    Romberg_Table(const func &f, const double &a, const double &b, const double &fa, const double &fb, unsigned n0) : n(n0), k(0)
    {
        if (!n0)
            throw "n不能为零!";
        double *p(arr = new double[n0]), h(b - a);
        *p = h * (fa + fb) / 2;
        unsigned i(n0);
        while (--i)
        {
            double pre_h(h), x(a + (h /= 2)), fx(f(x)), &I(*p);
            if (isnan(fx))
            {
                delete[] arr;
                throw "区间内存在奇点!";
            }
            while ((x += pre_h) < b)
                if (isnan(fx += f(x)))
                {
                    delete[] arr;
                    throw "区间内存在奇点!";
                }
            *++p = I / 2 + fx * h;
        }
    }
    ~Romberg_Table()
    {
        delete[] arr;
    }
    // 外推, 注意n不能等于1
    void extrapolation() noexcept
    {
        unsigned k4(1 << (++k << 1)), k41(k4 - 1), m(--n); // k4表示4^k
        double *p(arr);
        *p = (k4 * *(p + 1) - *p) / k41;
        while (--m)
        {
            ++p;
            *p = (k4 * *(p + 1) - *p) / k41;
        }
    }
    // 输出当前值
    inline double &get_value() noexcept
    {
        return arr[n - 1];
    }
    // 判断是否达到精度要求
    inline bool judge_accuracy(const double &e) noexcept
    {
        if (n == 1)
            return false;
        return abs(arr[n - 2] - arr[n - 1]) >= e;
    }
};

接着实现Romberg求积法:

/*
 * Romberg求积法(端点可能为奇点)
 * f :被积函数
 * a :积分下限
 * b :积分上限
 * fa:积分下限处的函数值
 * fb:积分上限处的函数值
 * e :精度
 * n :复化次数
 */
double Romberg(func f, const double &a, const double &b, const double &fa, const double &fb, const double &e = 1e-8, const unsigned &n = 16)
{
    if (a >= b)
        throw "区间上限小于区间下限!";
    Romberg_Table table(f, a, b, fa, fb, n);
    while (table.judge_accuracy(e))
        table.extrapolation();
    return table.get_value();
}
/*
 * Romberg求积法
 * f :被积函数
 * a :积分下限
 * b :积分上限
 * e :精度
 * n :复化次数
 */
double Romberg(func f, const double &a, const double &b, const double &e = 1e-8, const unsigned &n = 16)
{
    double fa(f(a));
    if (isnan(fa))
        throw "积分下限为奇点!";
    double fb(f(b));
    if (isnan(fb))
        throw "积分上限为奇点!";
    return Romberg(f, a, b, fa, fb, e, n);
}

其中, 对于积分上下限为奇点的情形提供了手动输入积分上下限函数值的接口.

测试案例

计算积分 I = ∫ 0 1 x ln ⁡ x d x \displaystyle I=\int_0^1\sqrt x\ln x{\rm d}x I=01x lnxdx.

#include <stdio.h>
// Romberg实现代码略
int main()
{
    auto f = [](double x) -> double
    {
        return sqrt(x) * log(x);
    };
    try
    {
        printf("%f", Romberg(f, 0., 1., 0., 0., 1e-6, 15));
    }
    catch (const char *s)
    {
        printf(s);
    }
    return 0;
}

最终得到结果为-0.444444, 计算过程如下:

k k k T 1 k T_1^k T1k T 2 k T_2^k T2k
00.000000-0.326753
1-0.245065-0.395784
2-0.358104-0.424752
3-0.408090-0.436603
4-0.429475-0.441361
5-0.438389-0.443244
6-0.442031-0.443981
7-0.443494-0.444267
8-0.444074-0.444377
9-0.444301-0.444419
10-0.444389-0.444435
11-0.444423-0.444441
12-0.444436-0.444443
13-0.444441-0.444444
14-0.444443-
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zsc_118

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值