zoukankan      html  css  js  c++  java
  • 自适应Simpson积分

    首先介绍一下Simpson积分的公式$$int_{a}^{b}f(x)dx approx frac {b-a} {6} (f(a)+4f(frac{a+b} {2})+f(b))$$

    可以用这个求比较平滑的曲线的积分的近似...

    但是直接用这个公式来做会误差很大

    具体用法是:

    用这个方法积$[l,mid]$,积$[mid,r]$,再积$[l,r]$判断是不是在误差范围内

    如果不在就递归地细化$[l,mid]$和$[mid,r]$

    比如说SDOI2017龙与地下城这道题

    就用这种方法积了一个正态分布曲线

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read()
    {
        int x=0;char ch=getchar();
        while(ch<'0' || '9'<ch)ch=getchar();
        while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
        return x;
    }
    
    typedef double db;
    
    db x,y;
    
    namespace Fast_Fouriet_Transform
    {
        typedef complex<db> cp;
        const int M=800009;
        const db pi=acos(-1);
    
        cp a[M],b[M];
        db per,ans[M];
        int m,l,rev[M];
    
        inline void FFT(cp *a,int n,int f)
        {
            for(int i=0;i<n;i++)
                if(i<rev[i])
                    swap(a[i],a[rev[i]]);
            for(int h=2;h<=n;h<<=1)
            {
                cp w(cos(2*pi/h),f*sin(2*pi/h));
                for(int j=0;j<n;j+=h)
                {
                    cp wn(1,0),x,y;
                    for(int k=j;k<j+(h>>1);k++)
                    {
                        x=a[k],y=wn*a[k+(h>>1)];
                        a[k]=x+y;
                        a[k+(h>>1)]=x-y;
                        wn*=w;
                    }
                }
            }
            if(f==-1)
                for(int i=0;i<n;i++)
                    a[i].real()/=(db)n;
        }
    
        int mian()
        {
            per=1.0/(db)x;
    
            for(m=1,l=0;m<(y*x);m<<=1)l++;
            for(int i=0;i<m;i++)
                rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
    
            a[0].real()=1.0;
            for(int i=0;i<x;i++)
                b[i].real()=per;
    
            FFT(a,m,1);FFT(b,m,1);
            for(int i=0;i<m;i++)
            {
                int cnt=y;
                while(cnt)
                {
                    if(cnt&1)
                        a[i]=a[i]*b[i];
                    b[i]=b[i]*b[i];
                    cnt>>=1;
                }
            }
            FFT(a,m,-1);
    
            ans[0]=a[0].real();
            for(int i=1;i<m;i++)
                ans[i]=a[i].real()+ans[i-1];
    
            for(int i=1;i<=10;i++)
            {
                int a=read(),b=read();
                if(a==0)
                    printf("%.8lf
    ",ans[b]);
                else
                    printf("%.8lf
    ",ans[b]-ans[a-1]);
            }
            return 0;
        }
    }
    
    namespace simpsons
    {
        const db pi2=sqrt(acos(-1)*2.0);
        const db eps=1e-12;
        db sigma,mu;
        inline db sqr(db x){return x*x;}
        inline db f(db x){return exp(-sqr(x)/2)/pi2;}
        inline bool dcmp(db x){return fabs(x)<15*eps;}
        inline void init()
        {
            sigma=sqrt((sqr(x)-1.0)/12.0);
            mu=(x-1.0)/2.0;
        }
    
        inline db simpson(db a,db b,db fl,db fmid,db fr)
        {
            return (b-a)*(fl+4.0*fmid+fr)/6.0;
        }
    
        inline db solve(db l,db r)
        {
            db mid=(l+r)/2.0,lm=(l+mid)/2.0,mr=(mid+r)/2.0;
            db fl=f(l),fmid=f(mid),fr=f(r),flm=f(lm),fmr=f(mr);
    
            db sipl=simpson(l,mid,fl,flm,fmid);
            db sipr=simpson(mid,r,fmid,fmr,fr);
            db sipm=simpson(l,r,fl,fmid,fr);
    
            if(dcmp(sipl+sipr-sipm))
                return sipl+sipr+(sipl+sipr-sipm)/15;
            else return solve(l,mid)+solve(mid,r);
        }
    
        int mian()
        {
            init();
            db base=solve(0,(x-1)*y);
            for(int i=1;i<=10;i++)
            {
                db a=(((db)read()-0.5)/y-mu)*sqrt(y)/sigma;
                db b=(((db)read()+0.5)/y-mu)*sqrt(y)/sigma;
                printf("%.8lf
    ",solve(0,b)-solve(0,a));
            }
        }
    }
    
    int main()
    {
        int T=read();
        while(T--)
        {
            scanf("%lf%lf",&x,&y);
            if(x*y<=400000)
                Fast_Fouriet_Transform::mian();
            else
                simpsons::mian();
        }
    
        return 0;
    }
    View Code

    还有经典的NOI2005月下柠檬树

    #include<iostream> 
    #include<cstdio> 
    #include<cstring> 
    #include<algorithm> 
    #include<cmath> 
    using namespace std; 
    #define MAXN 1010 
    double alpha; 
    int N,num; 
    #define INF 1e12 
    #define eps 1e-5 
    struct Point 
    { 
        double x,y; 
        Point (double X=0,double Y=0) {x=X; y=Y;} 
    }; 
    struct Circle 
    { 
        double r; 
        Point c; 
        Circle(Point C=(Point){0,0},double R=0) {c=C; r=R;} 
    }C[MAXN]; 
    struct Line 
    { 
        Point s,t; 
        double k,b; 
        Line(Point S=(Point){0,0},Point T=(Point){0,0})  
            { 
                s=S,t=T; 
                if (s.x>t.x) swap(s,t); 
                k=(s.y-t.y)/(s.x-t.x); 
                b=s.y-k*s.x; 
            } 
        double f(double x) {return k*x+b;} 
    }l[MAXN]; 
    int dcmp(double x) {if (fabs(x)<eps) return 0; return x<0? -1:1;} 
    double F(double x) 
    { 
        double re=0; 
        for (int i=1; i<=N; i++) //枚举圆是否与扫描线有交
            { 
                double d=fabs(x-C[i].c.x); 
                if (dcmp(d-C[i].r)>0) continue; 
                double len=2*sqrt(C[i].r*C[i].r-d*d); 
                re=max(re,len);  
            } 
        for (int i=1; i<=num; i++) //枚举公切线
            if (x>=l[i].s.x && x<=l[i].t.x) re=max(re,2*l[i].f(x)); 
        return re; 
    } //利用扫描线去判断
    double Calc(double l,double r) {double mid=(l+r)/2; return (F(l)+F(r)+F(mid)*4)*(r-l)/6;} 
    double Simpson(double l,double r,double now) 
    { 
        double mid=(l+r)/2; 
        double x=Calc(l,mid),y=Calc(mid,r); 
        if (!dcmp(now-x-y)) return now; 
        else return Simpson(l,mid,x)+Simpson(mid,r,y); 
    } 
    void Solve() 
    { 
        double L=INF,R=-INF; 
        for (int i=1; i<=N+1; i++) 
            L=min(L,C[i].c.x-C[i].r),R=max(R,C[i].c.x+C[i].r); 
    //  printf("%lf
    %lf
    ",L,R); 
        for (int i=1; i<=N; i++) 
            { 
                double d=C[i+1].c.x-C[i].c.x;  
                if (dcmp(d-fabs(C[i].r-C[i+1].r))<0) continue; //特判小圆被大圆覆盖的情况
                double sina=(C[i].r-C[i+1].r)/d,cosa=sqrt(1-sina*sina); 
                l[++num]=(Line){(Point){C[i].c.x+C[i].r*sina,C[i].r*cosa},(Point){C[i+1].c.x+C[i+1].r*sina,C[i+1].r*cosa}}; 
            } 
        printf("%.2lf
    ",Simpson(L,R,Calc(L,R))); 
    } 
    int main() 
    { 
        scanf("%d%lf",&N,&alpha); 
        double h,r; 
        for (int i=1; i<=N+1; i++) 
            scanf("%lf",&h), 
            C[i]=(Circle){((Point){(h/tan(alpha))+C[i-1].c.x,0}),0}; 
        for (int i=1; i<=N; i++) 
            scanf("%lf",&r),C[i].r=r; 
    //  for (int i=1; i<=N+1; i++) 
    //      printf("%d     %.2lf     %.2lf
    ",i,C[i].c.x,C[i].r); 
        Solve(); 
        return 0; 
    }
    View Code

    感觉...算个黑科技吧

    攒着以后可能会有点小用

  • 相关阅读:
    IOS设计模式之四(备忘录模式,命令模式)
    IOS设计模式之三(适配器模式,观察者模式)
    IOS设计模式之二(门面模式,装饰器模式)
    IOS设计模式之一(MVC模式,单例模式)
    C#调用C++导出(dllexport)方法
    C# 多任务之 Task
    C# Remoting的一个简单例子
    C#中指针使用总结
    C# fixed详解
    C#中virtual和abstract的区别
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/8456555.html
Copyright © 2011-2022 走看看