学习笔记: 自适应Simpson积分+例题

参考bloghttps://blog.csdn.net/lunch__/article/details/82288676
https://blog.csdn.net/xyz32768/article/details/81392369

这个东西对于三次以下的函数是正确的,但是对于三次以上的函数我们可以将其近似为低次函数套用Simpson公式,这个东西学名好像叫自适应Simpson积分。

昨天ACMACM模拟的时候遇到了一道SimpsonSimpson积分相关的题

完全不知道怎么求,我们组FishmenFishmen被BymBym嘲讽了很久

于是今天下午结合各种资料还是看了一下

这个东西不要觉得它看上去讲什么积分很高级 实际上认真推导也不是很难

首先SimpsonSimpson积分的式子是这个东西

积分号的下标代表区间左端点,上标代表区间右端点

这个值的意思就是函数在[a,b][a,b]区间与xx轴,x=ax=a, x=bx=b围成的面积

首先引入一些东西,SimpsonSimpson积分就是用一个二次曲线来无限逼近原曲线

来达到求得近似值的效果

首先引入一个东西 对于一个二次函数f(x)=ax2+bx+cf(x)=ax2+bx+c
求积分可得F(x)=∫x0f(x)dx=a3x3+b2x2+cx+DF(x)=∫0xf(x)dx=a3x3+b2x2+cx+D
在这里DD是一个常数 这个东西具体怎么证等我问了物理组的同学再来填坑

那么∫RLf(x)dx=F®−F(L)∫LRf(x)dx=F®−F(L)
∫RLf(x)dx=a3(R3−L3)+b2(R2−L2)+c(R−L)∫LRf(x)dx=a3(R3−L3)+b2(R2−L2)+c(R−L)
∫RLf(x)dx=(R−L)[a3(L2+R2+LR)+b2(L+R)+c]∫LRf(x)dx=(R−L)[a3(L2+R2+LR)+b2(L+R)+c]
∫RLf(x)dx=b−a6(2aL2+2aR2+2aLR+3bL+3bR+6c)∫LRf(x)dx=b−a6(2aL2+2aR2+2aLR+3bL+3bR+6c)
∫RLf(x)dx=b−a6{(aL2+bL+c)+(aR2+bR+c)+4[a(L+R2)2+b(L+R2)+c]}∫LRf(x)dx=b−a6{(aL2+bL+c)+(aR2+bR+c)+4[a(L+R2)2+b(L+R2)+c]}
∫RLf(x)dx=b−a6[f(L)+f®+4f(L+R2)]∫LRf(x)dx=b−a6[f(L)+f®+4f(L+R2)]
就这样证完了… 然后我们来考虑这个代码怎么实现

double Simpson(double a, double b) {
int mid = (a + b) / 2;
return (b - a) / 6 * (f(a) + f(b) + 4 * f(mid));
}
1
2
3
4
好了写完了
这样子直接计算我们发现误差非常大,毕竟原图像可能不能很好的用二次函数逼近

自适应Simpson积分
那怎么办呢,我们可以考虑模拟微分的过程,把区间分成无穷小份再把得到的答案积分就是最后的答案了

但是一般我们只要求一个近似值,所以我们就递归到满足正常误差即可

还有一个问题 每次递归下去都有精度误差,那么累加起来可能就和正确答案相差甚远了

这个时候我们可以在递归的时候提高精度要求,尽量让小的答案精确这样就可以了

double DFS(double L,double R,double eps){
    double ans=J(L,R);
    double mid=(L+R)/2;
    double ans1=J(L,mid),ans2=J(mid,R);
    if(fabs(ans1+ans2-ans)<eps*15) return ans1+ans2+(ans1+ans2-ans)/15;
    return DFS(L,mid,eps/2)+DFS(mid,R,eps/2);
}

在上式中eps一般取1e-6(具体看题题目的要求了,在下面的一道洛谷模板题中要求精度达到小数点后六位)
在这里插入图片描述
洛谷模板题
下面是AC的代码

//#include <cstdio>
//#include<cstring>
//#include<iostream>
//#include<algorithm>
//#include<set>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn = 0x3f3f3f3f;
const int N=1e6+10;
const double eps=1e-6;
double a,b,c,d,l,r;
double F(double x){
    return (c*x+d)/(a*x+b);
}
double J(double L,double R){
    double mid=(L+R)/2;
    return (R-L)/6*(F(R)+4*F(mid)+F(L));
}
double DFS(double L,double R,double eps){
    double ans=J(L,R);
    double mid=(L+R)/2;
    double ans1=J(L,mid),ans2=J(mid,R);
    if(fabs(ans1+ans2-ans)<eps*15) return ans1+ans2+(ans1+ans2-ans)/15;
    return DFS(L,mid,eps/2)+DFS(mid,R,eps/2);
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r);
    printf("%.6f\n",DFS(l,r,eps));
}

然后还有一道HDU1724的板子题
题意:题目就是求两条线所夹的椭圆的面积。这是个标准的椭圆,其中心在原点。我好像看到了题目连椭圆面积公式都给了,然而并没有任何用,直接上辛普森!
如果改变eps的大小,比如这道题eps=1e-6时46ms,eps=1e-4时15ms,看题目而定

//#include <cstdio>
//#include<cstring>
//#include<iostream>
//#include<algorithm>
//#include<set>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn = 0x3f3f3f3f;
const int N=1e6+10;
const double eps=1e-6;
double a,b;
double F(double x){
    return sqrt(b*b*(1-(x*x)/(a*a)));
}
double J(double L,double R){
    double mid=(L+R)/2;
    return (R-L)/6*(F(R)+F(L)+4*F(mid));
}

double DFS(double L,double R,double eps){
    double mid=(L+R)/2;
    double ans=J(L,R),ans1=J(L,mid),ans2=J(mid,R);
    if((ans1+ans2-ans)<=eps*15) return ans1+ans2+(ans1+ans2-ans)/15;
    return DFS(L,mid,eps/2.0)+DFS(mid,R,eps/2.0);
}
int main(){
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    double l,r;
    int t;
    scanf("%d",&t);
    while(t--){
    scanf("%lf%lf%lf%lf",&a,&b,&l,&r);
    printf("%.3f\n",2.0*DFS(l,r,eps));
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 自适应Simpson积分是一种数值积分方法,可以用于计算函数在给定区间上的积分值。在Matlab中,可以使用内置函数“quad”来实现自适应Simpson积分。具体步骤如下: 1. 定义被积函数f(x)。 2. 使用“quad”函数进行积分计算,语法为: quad(f,a,b) 其中,f为被积函数,a和b为积分区间的上下限。 3. 如果需要更高的精度,可以使用“quadl”函数进行积分计算,语法为: quadl(f,a,b,tol) 其中,tol为误差容限,即当积分误差小于tol时,积分计算停止。 需要注意的是,自适应Simpson积分方法适用于连续函数,如果被积函数在积分区间上不连续或有间断点,需要进行分段积分。 ### 回答2: 自适应Simpson积分是一种常用的数值积分方法,它基于辛普森公式,能够在不同精度要求下,自适应地选取合适的分割数,从而达到更高的精度。在Matlab中实现自适应Simpson积分需要以下步骤: 1. 编写Simpson积分的函数subSimpson,该函数接受一个函数句柄和积分上下限为参数,返回积分值。 2. 设置初始的精度要求tol和分割数N0。将整个积分区间[a,b]均匀地分割成N0个子区间,计算每个子区间的积分值,求和得到这一级别的积分结果S1。 3. 将整个积分区间再次均匀地分割,得到2N0个子区间。根据辛普森公式计算每个子区间的积分值,并利用这些结果得到更加精细的积分值S2。 4. 计算收敛因子Q=(S2-S1)/15,如果Q小于指定的精度要求tol,则返回S2作为积分结果;否则,将积分区间再次分割,进入第三步。 5. 在分割过程中,可以记录下每一级别的积分值和分割数,以便于后续的分析和统计。 Matlab代码示例: ```matlab function [I,n] = adaptSimpson(f,a,b,tol,N0) % f: 被积函数 % a,b: 积分区间 % tol: 精度要求 % N0: 初始分割数 x = linspace(a,b,2*N0+1); h = (b-a)/N0/2; y = f(x); S1 = sum(y(1:2:end-2)+4*y(2:2:end-1)+y(3:2:end)); S2 = sum(y(1:2:end-2)+4*y(2:2:end-1)+2*y(3:2:end-1)+4*y(4:2:end)+y(5:2:end)); I = S2/15*h; n = N0; Q = (S2-S1)/15; while abs(Q) > tol N = 2*n; x = linspace(a,b,2*N+1); y = f(x); S1 = S2; S2 = sum(y(1:2:end-2)+4*y(2:2:end-1)+2*y(3:2:end-1)+4*y(4:2:end)+y(5:2:end)); I = S2/15*h; n = N; Q = (S2-S1)/15; end end ``` 代码中,我们先根据初始的分割数N0,计算出积分区间的所有节点x,并根据辛普森公式计算出这一级别的积分结果S1和下一级别的积分结果S2。如果S2满足精度要求,则直接返回;否则,我们将分割数翻倍,重新计算节点和积分结果,进入下一级别的计算。当S2满足精度要求后,我们返回积分结果I和分割数n。 总之,自适应Simpson积分是一种高效的数值积分方法,通过递归的方式自适应地调整分割数,从而实现更高的精度。在Matlab中实现自适应Simpson积分,需要熟悉辛普森公式和递归算法的基本原理。 ### 回答3: 自适应Simpson积分是一种数值积分方法,可用于计算函数的定积分。该方法是通过将定积分区间分割成若干个子区间,每个子区间采用Simpson公式来计算积分值,从而得到整个区间的积分值。与传统的Simpson积分相比,自适应Simpson积分可以更准确地估计积分值,且能够自动适应积分函数的变化以提高计算效率。 在Matlab中,实现自适应Simpson积分的基本思路如下: 1. 将积分区间[a,b]分割成若干个子区间,每个子区间采用Simpson公式计算积分值I1。 2. 将整个区间[a,b]再分割成若干个子区间,每个子区间采用两个子区间的Simpson积分值之和减去一个子区间的Simpson积分值来计算积分值I2。 3. 计算误差E=abs(I2-I1)/15,如果E大于预设的误差精度tolerance,则将整个区间继续分割,否则返回I2作为最终的积分值。 该方法的优点在于能够自动适应积分函数的变化,从而提高计算精度和效率。具体实现时,需要使用递归方法来实现自动区间分割,同时需要设置适当的最大分割次数以避免程序陷入死循环。 Matlab中自适应Simpson积分的函数形式如下: function[result, err] = adaptive_simpson(f, a, b, tolerance, max_depth) % f为要积分的函数,a和b为积分区间的下限和上限,tolerance为误差精度,max_depth为最大分割次数 depth = 1; % 初始化分割次数为1 [result, err] = simpson_rule(f, a, b); % 计算初始的积分值和误差 while (depth < max_depth) && (err > tolerance) % 如果分割次数未达到最大值且误差仍然大于误差精度 depth = depth + 1; % 分割次数加1 [result_left, err_left] = simpson_rule(f, a, (a+b)/2); % 计算左半区间的积分值和误差 [result_right, err_right] = simpson_rule(f, (a+b)/2, b); % 计算右半区间的积分值和误差 result = result_left + result_right; % 计算整个区间的积分值 err = err_left + err_right; % 计算整个区间的误差 end 其中simpson_rule函数为Simpson积分公式的实现,具体如下: function[result, err] = simpson_rule(f, a, b) % f为要积分的函数,a和b为积分区间的下限和上限 result = (b-a)/6 * (f(a) + 4*f((a+b)/2) + f(b)); % 计算Simpson积分值 err = (b-a)^5 / 2880 * max(abs(diff(f([a,b,(a+b)/2])))); % 计算误差 end 需要注意的是,在实现自适应Simpson积分时,需要确保积分函数在积分区间内具有充分的平滑性和连续性,否则可能导致计算误差过大或发生计算异常。因此在应用该方法时,需要首先对积分函数进行充分的分析和预处理,以保证计算结果的准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值