辛普森自适应积分

前言:

神奇的东西qwq

基本概念:

a b f ( x ) d x 这个式子表示在区间 a b上对函数f(x)求定积分.
怎么求呢?
高中课本里告诉我们
a b f ( x ) d x = F ( a ) F ( b ) F(x)为f(x)的原函数,也就是F(x)的导函数为f(x)

然而某些函数的原函数极其难求.
比如要求原的函数长得很复杂,要求的原函数非初等函数.

那么这个时候我们就不能去准确的求出这个积分值了,而是要引入一种近似化的思想.
定积分f(x)的定义也就是曲线f(x)下方的面积(假设f(x)恒大于0)
这个时候我们就需要引入一种近似化求这个面积的方法–辛普森积分法

在区间[a,b]内等间距的取奇数个点
a = x 0 , x 1 , x 2 , x 3... x n = b 间距为 Δ x
那么 a b f ( x ) d x Δ x 3 ( y 0 + 4 y 1 + y 2 ) + Δ x 3 ( y 2 + 4 y 3 + y 4 ) + . . . + Δ x 3 ( y n 2 + 4 y n 1 + y n )
精确度与取的点数成正比.
如果只取三点,那么
a b f ( x ) d x b a 6 ( f ( a ) + 4 f ( b a 2 ) + f ( b ) )
这就是三点辛普森公式,又简称辛普森公式.
当然我们如果只取三点显然精度不够,多了又会导致Tle
那么到底取多少个合适呢?

辛普森积分有个很重要的变种,叫做辛普森自适应积分,具体来说就是根据一些方法,使得在容易近似的区间少划分积分,不容易近似的区间多划分几分,也就是所谓的自适应了.

具体的话也就是

c = ( a + b ) / 2

| s ( a , c ) + s ( c , b ) s ( a , b ) | <= 15 E P S 就说明本区间的进行三点划分即可,否则继续递归划分,EPS也需要/2

返回结果为
s ( a , c ) + s ( c , b ) + ( s ( a , c ) + s ( c , b ) s ( a , b ) ) / 15.0 )

直接返回 s ( a , b ) 的话,貌似误差偏大

代码实现:

inline double f(double x)
{
    /*函数表达式*/
}
inline double simpson(double l,double r)
{
    double mid=(l+r)/2.0;
    return (f(l)+4.0*f(mid)+f(r))*(r-l)/6.0;
} 
inline double solve(double l,double r,double eps)
{
    double mid=(l+r)/2.0;
    double s=simpson(l,r),sl=simpson(l,mid),sr=simpson(mid,r);
    if(std::fabs(sl+sr-s)<=15.0*eps) return (sl+sr+(sl+sr-s)/15.0);
    return solve(l,mid,eps/2.0)+solve(mid,r,eps/2.0);
}

例题:

Hdu 1724
求一个区间的椭圆面积,可以直接计算椭圆在X轴以上的面积,最后答案*2
在上方的椭圆轨迹可以看做一个函数,写出Y关于X的表达式,然后套用辛普森自适应积分求解即可

Ac 代码:

#include <cmath>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
double a,b;
inline double f(double x)
{
    double x1=sqrt(1.0-(x/a)*(x/a));
    return b*x1;
}
inline double simpson(double l,double r)
{
    double mid=(l+r)/2.0;
    return (f(l)+4.0*f(mid)+f(r))*(r-l)/6.0;
} 
inline double solve(double l,double r,double eps)
{
    double mid=(l+r)/2.0;
    double s=simpson(l,r),sl=simpson(l,mid),sr=simpson(mid,r);
    if(std::fabs(sl+sr-s)<=15.0*eps) return (sl+sr+(sl+sr-s)/15.0);
    return solve(l,mid,eps/2.0)+solve(mid,r,eps/2.0);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        double l,r;
        scanf("%lf%lf%lf%lf",&a,&b,&l,&r);
        printf("%.3lf\n",2.0*solve(l,r,1e-5));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35914587/article/details/79929685
今日推荐