zoukankan      html  css  js  c++  java
  • 自适应辛普森积分

    辛普森积分

    自适应辛普森积分是一种解决定积分求解问题的算法。

    给出一个函数(f(x)),求:

    [int _l^rf(x){ m{d}}x ]

    我们考虑用一条抛物线来近似这个函数,设(g(x)=ax^2+bx+c)

    那么可得:

    [egin{align} int_l^rf(x){ m{d}}x&approx int_l^rg(x){ m{d}}x\ &=frac{1}{3}a(r^3-l^3)+frac{1}{2}b(r^2-l^2)+c(r-l)\ &=frac{(r-l)(al^2+bl+c+ar^2+br+c+a(l+r)^2+2b(l+r)+4c}{6}\ &=frac{(r-l)(f(l)+f(r)+4f(frac{l+r}{2}))}{6} end{align} ]

    那么这个玩意就叫(simpson)公式。

    自适应辛普森积分

    上面这个玩意显然精度不够,甚至可以说根本就不对。

    那么怎么解决这个问题呢,我们可以考虑模拟微分的过程,把定义域切成一小段一小段,然后在近似,这样精度就有了。

    但是显然这样是很慢的,所以我们有了一种自适应的想法,即若当前区间精度够了就退出,否则二分然后递归计算左右两个区间,具体来说,代码应该这么写:

    double _int (double l,double r) {return (r-l)*(f(l)+f(r)+4.0*f((l+r)/2.0))/6.0;}
    double calc(double l,double r,double ans) {
    	double mid=(l+r)/2.0,L=_int(l,mid),R=_int(mid,r);
    	if(fabs(L+R-res)<eps) return res;
    	else return calc(l,mid,L)+calc(mid,r,R);
    }
    

    但是这样精度可能还是不够,然后有一种更玄学的近似,具体证明我也不会...

    代码具体长这样:

    double calc(double l,double r,double res,double Eps) {
    	double mid=(l+r)/2.0,L=_int(l,mid),R=_int(mid,r);
    	if(fabs(L+R-res)<Eps*15.0) return L+R+(L+R-res)/15.0;
    	else return calc(l,mid,L,Eps*0.5)+calc(mid,r,R,Eps*0.5);
    }
    

    例题

    题目链接:luogu P4525

    这题就直接套公式就好了。

    #include<bits/stdc++.h>
    using namespace std;
     
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
     
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    #define lf double 
    
    const int maxn = 2e5+10;
    const lf eps = 1e-9;
    
    lf a,b,c,d;
    
    lf f(lf x) {return (c*x+d)/(a*x+b);}
    
    lf integral(lf L,lf R) {return (R-L)*(f(L)+f(R)+4.0*f((L+R)/2.0))/6.0;}
    
    lf calc(lf L,lf R,lf ans,lf Eps) {
    	lf mid=(L+R)/2.0;
    	lf l=integral(L,mid),r=integral(mid,R);
    	if(fabs(l+r-ans)<Eps*15.0) return l+r+(l+r-ans)/15.0;
    	else return calc(L,mid,l,Eps*0.5)+calc(mid,R,r,Eps*0.5);
    }
    
    int main() {
    	lf L,R;
    	scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&L,&R);
    	printf("%.6lf
    ",calc(L,R,integral(L,R),eps));
    	return 0;
    }
    

    不过这个积分很容易积出来:

    [egin{align} intfrac{ax+b}{cx+d}{ m{d}}x&=intfrac{c}{a}-frac{d-frac{bc}{a}}{ax+b}{ m{d}}x\ &=frac{cx}{a}-frac{1}{a}(d-frac{bc}{a})ln|ax+b|+C end{align} ]

    注意到中间有一个很简单的换元:

    [int frac{1}{ax+b}{ m{d}}x=frac{1}{a}intfrac{1}{ax+b}{ m{d}}(ax+b)=frac{1}{a}ln|ax+b| ]


    题目链接:luogu P4526

    注意到(a<0)时,(lim_{x o 0}f(x)=+infty),此时积分发散。

    否则积分收敛,我也不是特别清楚为什么

    那么直接套公式就好了。

    #include<bits/stdc++.h>
    using namespace std;
     
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
     
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    #define lf double 
    
    const int maxn = 2e5+10;
    const lf eps = 1e-8;
    
    lf a;
    
    lf f(lf x) {return pow(x,a/x-x);}
    lf _int(lf l,lf r) {return (r-l)*(f(l)+f(r)+4.0*f((l+r)/2.0))/6.0;}
    lf calc(lf l,lf r,lf res,lf Eps) {
    	lf mid=(l+r)/2.0,L=_int(l,mid),R=_int(mid,r);
    	if(fabs(L+R-res)<eps*15.0) return L+R+(L+R-res)/15.0;
    	else return calc(l,mid,L,Eps*0.5)+calc(mid,r,R,Eps*0.5);
    }
    
    int main() {
    	scanf("%lf",&a);
    	if(a<0) puts("orz");
    	else printf("%.5lf
    ",calc(eps,20.0,_int(eps,20.0),eps));
    	return 0;
    }
    
  • 相关阅读:
    javascript 延迟
    ashx获取另一个页面的返回内容
    Handler实现数据模板
    font-face 在 Firefox无法正常工作问题
    离线缓存 manifest
    事件 event
    源码学习
    调试
    Icon font font face
    aria-label
  • 原文地址:https://www.cnblogs.com/hbyer/p/10538167.html
Copyright © 2011-2022 走看看