zoukankan      html  css  js  c++  java
  • 辛普森积分学习笔记

    白书上也有讲,就是一个在精度要求不高的时候计算积分的算法。

    推荐博客:https://blog.csdn.net/VictoryCzt/article/details/80660113

    我感觉其实功利一点的话,不用完全理解它的原理,大概明白然后会套板子做题即可(蒟蒻口胡)。

    洛谷P4525

    模板题,直接上模板。

    #include<bits/stdc++.h>
    using namespace std;
    const double eps=1e-7;
    double a,b,c,d,L,R;
    
    double f(double x) {  //具体计算公式 
        return (c*x+d)/(a*x+b);
    }
    
    double simpson(double l,double r) {  //辛普森积分公式 
        return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
    }
    
    double asr(double l,double r,double exps,double val) {  //自适应辛普森积分 
        double mid=(l+r)/2;
        double lval=simpson(l,mid),rval=simpson(mid,r);
        if (fabs(lval+rval-val)<=15*exps) return lval+rval+(lval+rval-val)/15;
        else return asr(l,mid,exps/2,lval)+asr(mid,r,exps/2,rval);
    }
    
    int main()
    {
        scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&L,&R);
        printf("%.6lf
    ",asr(L,R,eps,simpson(L,R)));
        return 0;
    }
    View Code

    洛谷P4526

    另一道模板题,不过辛普森积分好像不适用于发散函数和反常积分(积分区间无穷大)。所以我们要想办法处理这两种情况。①可以证明a<0函数发散,a>0函数收敛。②x到底大于某个数之后变得非常非常小,于是之后的就可以不积分了。于是这题就可以做了。

    #include<bits/stdc++.h>
    using namespace std;
    const double eps=1e-7;
    double a;
    
    double f(double x) {  //具体计算公式 
        return pow(x,a/x-x);
    }
    
    double simpson(double l,double r) {  //辛普森积分公式 
        return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
    }
    
    double asr(double l,double r,double exps,double val) {  //自适应辛普森积分 
        double mid=(l+r)/2;
        double lval=simpson(l,mid),rval=simpson(mid,r);
        if (fabs(lval+rval-val)<=15*exps) return lval+rval+(lval+rval-val)/15;
        else return asr(l,mid,exps/2,lval)+asr(mid,r,exps/2,rval);
    }
    
    int main()
    {
        scanf("%lf",&a);
        if (a<0) puts("orz");
        else printf("%.5lf
    ",asr(1e-10,20,eps,simpson(1e-10,20)));
        return 0;
    }
    View Code

    HDU-1724

    求椭圆面积一部分。精度要求很低,用辛普森积分。

    #include<bits/stdc++.h>
    using namespace std;
    const double eps=1e-7;
    double a,b,l,r;
    
    double f(double x) {  //具体计算公式 
        return 2*b*sqrt(1-x*x/a/a);
    }
    
    double simpson(double l,double r) {  //辛普森积分公式 
        return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
    }
    
    double asr(double l,double r,double exps,double val) {  //自适应辛普森积分 
        double mid=(l+r)/2;
        double lval=simpson(l,mid),rval=simpson(mid,r);
        if (fabs(lval+rval-val)<=15*exps) return lval+rval+(lval+rval-val)/15;
        else return asr(l,mid,exps/2,lval)+asr(mid,r,exps/2,rval);
    }
    
    int main()
    {
        int T; cin>>T;
        while (T--) {
            scanf("%lf%lf%lf%lf",&a,&b,&l,&r);
            printf("%.3lf
    ",asr(l,r,eps,simpson(l,r)));
        }
        return 0;
    }
    View Code

    BZOJ-2178

    求圆的面积并。直接在x轴上积分,然后f函数就是直线经过的线段长度。其他还是套辛普森积分即可。

    但是这题有非常多细节:第一,要先把被包含的圆去掉。第二,可以看出每次计算f函数都要耗费大量时间,所以要想办法通过尽量复用f函数值来减少计算次数。第三,这也是蒟蒻没想懂的一点,好像用我上面板子的控制精度方法是会超时的(即每次二分将精度也除以2的方法),这题可以用一个确切的精度1e-13然后一直用这个精度控制即可。

    #include<bits/stdc++.h>
    using namespace std;
    const double eps=1e-13;
    const int N=1e3+10;
    int n;
    double x[N],y[N],r[N];
    bool ban[N];
    
    struct dat{
        double x,y;
        bool operator < (const dat &rhs) const {
            return x<rhs.x;
        }
    }A[N<<1];
    double f(double P) {  //计算直线P经过线段长度 
        int num=0;
        for (int i=1;i<=n;i++) {
            if (fabs(x[i]-P)>=r[i]) continue;
            double d=fabs(x[i]-P);
            double l=sqrt(r[i]*r[i]-d*d);
            A[++num].x=y[i]-l; A[num].y=y[i]+l;
        }
        sort(A+1,A+num+1);
        double ret=0,lst=-0x3f3f3f3f;
        for (int i=1;i<=num;i++)
            if (A[i].x>lst) ret+=A[i].y-A[i].x,lst=A[i].y;
            else if (A[i].y>lst) ret+=A[i].y-lst,lst=A[i].y;
        return ret;
    }
    
    double simpson(double l,double r) {  //辛普森积分公式 
        return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
    }
    
    double cal(double l,double r,double fl,double fr,double fm){
        return (fl+fr+4*fm)*(r-l)/6;
    }
    
    double asr(double l,double r,double val,double L,double R,double M) {  //自适应辛普森积分 
        double mid=(l+r)/2,LM=f((l+mid)/2),RM=f((mid+r)/2);
        double lval=cal(l,mid,L,M,LM),rval=cal(mid,r,M,R,RM);
        if (fabs(lval+rval-val)<=eps) return lval+rval+(lval+rval-val)/15;
        return asr(l,mid,lval,L,M,LM)+asr(mid,r,rval,M,R,RM);
    }
    
    int main()
    {
        cin>>n;
        double L=0x3f3f3f3f,R=-0x3f3f3f3f;
        for (int i=1;i<=n;i++) {
            scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
            L=min(L,x[i]-r[i]);
            R=max(R,x[i]+r[i]);
        }
        
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++) 
            if (!ban[i] && !ban[j]) {
                double dist=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
                if (dist>fabs(r[i]-r[j])) continue;
                if (r[j]<=r[i]) ban[j]=1; else ban[i]=1;
            }
        int t=0;
        for (int i=1;i<=n;i++)
            if (!ban[i]) { t++; x[t]=x[i]; y[t]=y[i]; r[t]=r[i]; }
        n=t;        
        
        double M=(L+R)/2;
        printf("%.3lf
    ",asr(L,R,simpson(L,R),f(L),f(R),f(M)));
        return 0;
    }
    View Code

    洛谷P4207/BZOJ-1502

  • 相关阅读:
    FreeCommander 学习手册
    String详解, String和CharSequence区别, StringBuilder和StringBuffer的区别 (String系列之1)
    StringBuffer 详解 (String系列之3)
    StringBuilder 详解 (String系列之2)
    java io系列26之 RandomAccessFile
    java io系列25之 PrintWriter (字符打印输出流)
    java io系列24之 BufferedWriter(字符缓冲输出流)
    java io系列23之 BufferedReader(字符缓冲输入流)
    java io系列22之 FileReader和FileWriter
    java io系列21之 InputStreamReader和OutputStreamWriter
  • 原文地址:https://www.cnblogs.com/clno1/p/10914919.html
Copyright © 2011-2022 走看看