辛普森积分(自适应辛普森公式求积分)

来源:互联网 发布:数据库设计规范 编辑:程序博客网 时间:2024/04/27 21:59

自适应辛普森公式求积分

第一回接触辛普森积分,至于这个辛普森是干嘛的呢,在这里就有必要好好地讲一讲了。

来源:辛普森(Simpson)公式是牛顿-科特斯公式当n=2时的情形,也称为三点公式。利用区间二等分的三个点来进行积分插值。其科特斯系数分别为1/6,4/6,1/6。

应用:立体几何中用来求拟柱体体积的公式。

这里就不详细说辛普森公式了,有需要的朋友可以看这里:https://baike.baidu.com/item/%E8%BE%9B%E6%99%AE%E6%A3%AE%E5%85%AC%E5%BC%8F/9255085?fr=aladdin

接下来我们好好的说说自适应辛普森公式求积分自适应辛普森公式求积分是很重要的一个知识点,弄懂了自适应辛普森公式求积分就能明白辛普森积分是干嘛的了,

下面的内容是重点!!!:

假设我们求以下积分:

这里写图片描述

比较特殊的情况,就是可以推导出来最后的形式。但是比较一般的情况是,我们只能大致得到一个XY坐标系里的曲线,我们求的就是曲线和X轴所围成的面积。

因此我们有自适应辛普森公式,他会根据实际情况来自动的调整精度。

它的大致过程就是,给定一个要求达到的精度eps,算法就会根据实际情况递归的划分区间。容易近似的地方少划分,不容易近似的地方多划分几份。

具体来讲,我们在以下情况下直接返回结果,否则递归划分区间:

这里写图片描述

具体参考博客:http://blog.csdn.net/frosero/article/details/45799135

下面举个例题:hdu-1724

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1724

题目大意:题目就是求两条线所夹的椭圆的面积。这是个标准的椭圆,其中心在原点。我好像看到了题目连椭圆面积公式都给了,然而并没有任何用,直接上辛普森!

根据椭圆的对称性,我们求x轴上方的面积然后乘以2就是答案。根据椭圆方程x2a2+y2b2=1,构造函数y=。。。其中y>0,然后对此函数进行自适应辛普森积分就可以了。

写完程序过了样例后立马交,TLE,1000ms。结果发现是Eps取的太小了,取了1e-10,随着递归下去,Eps不断除以2,导致不断递归划分,做的次数很多就超时了。这题只要求保留3位小数,所以精度没有太大问题,将Eps改为1e-5立马就AC了,109ms。

ac代码:

#include <iostream>#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <algorithm>#include <set>#include <map>#include <vector>#include <queue>#define ri(n) scanf("%d",&n)#define oi(n) printf("%d\n",n)#define rl(n) scanf("%lld",&n)#define ol(n) printf("%lld\n",n)#define rep(i,l,r) for(i=l;i<=r;i++)#define rep1(i,l,r) for(i=l;i<r;i++)using namespace std;typedef long long ll;const int inf=0x3f3f3f3f;const double eps=1e-5;//这里的eps是整个辛普森应用的关键,至于取多大,得依题目而定double a,b,l,r;double f(double x)//这里的f函数是自己根据题目推导出来的,主要还是x有关y的函数{    double y=b*sqrt(1.0-(x*x)/(a*a));    return y;}double simpson(double a,double b)//这里是辛普森公式{    double c=(a+b)/2.0;    return (f(a)+f(b)+4.0*f(c))*(b-a)/6.0;}double ars(double a,double b,double eps)//这里就是递归求辛普森最重要的一段函数了,{    double c=(a+b)/2.0;    double mid=simpson(a,b),l=simpson(a,c),r=simpson(c,b);    if(fabs(l+r-mid)<=15*eps)        return l+r+(l+r-mid)/15.0;    return ars(a,c,eps/2.0)+ars(c,b,eps/2.0);}int main(){    int t;    ri(t);    while(t--)    {        scanf("%lf%lf%lf%lf",&a,&b,&l,&r);        printf("%.3lf\n",2.0*ars(l,r,eps));//递归求辛普森    }    return 0;}