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

    一个完全不会计算几何的蒟蒻的自我拯救……

    辛普森积分

    有的时候会有一些毒瘤计算几何题,要求的图形面积边缘是一段函数,而这个函数解析式通常非常繁琐,没办法直接用公式积分,所以就需要用辛普森积分求近似值。

    辛普森积分的用途就是在精度要求不高的时候(通常是求图形面积)求函数积分的近似值,大概步骤就是在积分区间$[a,b]$中不断二分,每次找$a,b,frac{a+b}{2}$三点用一条抛物线来拟合原函数,当误差小于eps的时候就返回答案。

    辛普森积分公式

    $$int_{a}^{b}f(x)dxapproxfrac{b-a}{6} imesleft[f(a)+f(b)+4fleft(frac{a+b}{2} ight) ight]$$

    下面推导一波:

    设$g(x)$是一个关于$x$的二次函数,且$g(x)=Ax^2+Bx+C$,对于定积分$int_{0}^{a}g(x)dx$求积得$frac{Ax^3}{3}+frac{Bx^2}{2}+Cx+D$,其中$D$为常数可以忽略;

    令$H(x)=int_{0}^{a}g(x)dx$,则求其中的一段定积分就有$int_{a}^{b}g(x)dx=H(b)-H(a)$;

    设$g(x)$是当前的拟合函数,那么有:

    $$int_{a}^{b}f(x)dxapproxint_{a}^{b}g(x)dx$$

    $$=H(b)-H(a)$$

    $$=frac{A}{3}(b^3-a^3)+frac{B}{2}(b^2-a^2)+C(b-a)$$

    $$=frac{b-a}{6} imesleft[2A(a^2+ab+b^2)+3B(a+b)+6C ight]$$

    然后大力拆括号配方:

    $$=frac{b-a}{6} imesleft[(Aa^2+Ba+C)+(Ab^2+Bb+C)+A(a^2+2ab+b^2)+2B(a+b)+4C ight]$$

    $$=frac{b-a}{6} imesleft[g(a)+g(b)+4Aleft(frac{a+b}{2} ight)^2+4Bleft(frac{a+b}{2} ight)+4C ight]$$

    $$=frac{b-a}{6} imesleft[g(a)+g(b)+4gleft(frac{a+b}{2} ight) ight]$$

    在实际使用的时候,$g(x)$拟合的曲线可以用$f(x)$代替,因此:

    $$int_{a}^{b}f(x)dxapproxfrac{b-a}{6} imesleft[f(a)+f(b)+4fleft(frac{a+b}{2} ight) ight]$$

    实现:

    具体实现就是不断二分区间,用辛普森积分公式求出近似值,当精度达到要求后就返回;

    写起来很方便,核心代码就十几行左右……

    注意用辛普森的时候要大力调eps,否则可能会在WA和TLE之间徘徊……

    代码:

    模板:洛咕P4525 自适应辛普森法1

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 double a,b,c,d,l,r;
    12 double f(double x){
    13     return (c*x+d)/(a*x+b);
    14 }
    15 double simpson(double l,double r){
    16     return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
    17 }
    18 double sint(double l,double r,double v){
    19     double mid=(l+r)/2,lv=simpson(l,mid),rv=simpson(mid,r);
    20     if(fabs(lv+rv-v)<=eps){
    21         return v;
    22     }
    23     return sint(l,mid,lv)+sint(mid,r,rv);
    24 }
    25 double getint(double l,double r){
    26     return sint(l,r,simpson(l,r));
    27 }
    28 int main(){
    29     scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r);
    30     printf("%.6lf",getint(l,r));
    31     return 0;
    32 }

    一个精度更高的做法:

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 //#define eps 1e-7
     9 using namespace std;
    10 typedef long long ll;
    11 const double eps=1e-7;
    12 double a,b,c,d,l,r;
    13 double f(double x){
    14     return (c*x+d)/(a*x+b);
    15 }
    16 double simpson(double l,double r){
    17     return (f(l)+f(r)+4*f((l+r)/2))*(r-l)/6;
    18 }
    19 double sint(double l,double r,double ep,double v){
    20     double mid=(l+r)/2,lv=simpson(l,mid),rv=simpson(mid,r);
    21     if(fabs(lv+rv-v)<=ep*15){
    22         return lv+rv+(lv+rv-v)/15;
    23     }
    24     return sint(l,mid,ep/2,lv)+sint(mid,r,ep/2,rv);
    25 }
    26 double getint(double l,double r,double ep){
    27     return sint(l,r,ep,simpson(l,r));
    28 }
    29 int main(){
    30     scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r);
    31     printf("%.6lf",getint(l,r,eps));
    32     return 0;
    33 }

    例题

    【BZOJ1502】【NOI2005】月下柠檬树

    若干个圆台投影之后形状大概是若干个圆加外公切线围成的图形,然后最顶上是个圆锥,整个图形是轴对称的,大力辛普森积分就好了;

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 typedef double db;
    12 struct cir{
    13     db x,y,xx,yy;
    14 }a[1001];
    15 int n,tot=0;
    16 db al,mi,mx,t,h[1001],s[1001],r[1001];
    17 db f(db x){
    18     db ret=0;
    19     for(int i=0;i<=n;i++){
    20         if(fabs(s[i]-x)<r[i]){
    21             ret=max(ret,sqrt(r[i]*r[i]-(s[i]-x)*(s[i]-x)));
    22         }
    23     }
    24     for(int i=1;i<=tot;i++){
    25         if(x>a[i].x&&x<a[i].xx){
    26             ret=max(ret,a[i].y+(a[i].yy-a[i].y)*(x-a[i].x)/(a[i].xx-a[i].x));
    27         }
    28     }
    29     return ret;
    30 }
    31 db sim(db l,db r,db fl,db fm,db fr){
    32     return (fl+fr+4*fm)*(r-l)/6;
    33 }
    34 db sint(db l,db r,db fl,db fm,db fr){
    35     db mid=(l+r)/2,L=f((l+mid)/2),R=f((mid+r)/2),s=sim(l,r,fl,fm,fr),sl=sim(l,mid,fl,L,fm),sr=sim(mid,r,fm,R,fr);
    36     if(fabs(sl+sr-s)<eps)return sl+sr;
    37     return sint(l,mid,fl,L,fm)+sint(mid,r,fm,R,fr);
    38 }
    39 int main(){
    40     scanf("%d%lf",&n,&al);
    41     for(int i=0;i<=n;i++){
    42         scanf("%lf",&h[i]);
    43         if(i)h[i]+=h[i-1];
    44         s[i]=h[i]/tan(al);
    45     }
    46     for(int i=0;i<n;i++){
    47         scanf("%lf",&r[i]);
    48         mi=min(mi,s[i]-r[i]);
    49         mx=max(mx,s[i]+r[i]);
    50     }
    51     mx=max(mx,s[n]);
    52     for(int i=0;i<n;i++){
    53         t=s[i+1]-s[i];
    54         if(t>fabs(r[i+1]-r[i])){
    55             a[++tot].x=s[i]-r[i]*(r[i+1]-r[i])/t;
    56             a[tot].y=sqrt(r[i]*r[i]-(a[tot].x-s[i])*(a[tot].x-s[i]));
    57             a[tot].xx=s[i+1]-r[i+1]*(r[i+1]-r[i])/t;
    58             a[tot].yy=sqrt(r[i+1]*r[i+1]-(a[tot].xx-s[i+1])*(a[tot].xx-s[i+1]));
    59         }
    60     }
    61     printf("%.2lf",sint(mi,mx,0,f((mi+mx)/2),0)*2);
    62     return 0;
    63 }

    【BZOJ2178】圆的面积并

    先把被包含的圆去掉,然后大力切圆就好了

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 typedef double db;
    12 struct node{
    13     db x,y;
    14     node(){}
    15     node(db _x,db _y){x=_x,y=_y;}
    16     friend bool operator <(node a,node b){
    17         return (fabs(a.x-b.x)<eps)?a.y<b.y:a.x<b.x;
    18     }
    19 }p[1001];
    20 struct cir{
    21     db x,y,r;
    22     bool ch;
    23     friend bool operator <(cir a,cir b){
    24         return a.x<b.x;
    25     }
    26     node getf(db xx){
    27         if(fabs(x-xx)>=r)return node(0,0);
    28         return node(y-sqrt(r*r-(x-xx)*(x-xx)),y+sqrt(r*r-(x-xx)*(x-xx)));
    29     }
    30 }c[1001];
    31 int n;
    32 db mi=inf,mx=-inf;
    33 db dis(cir a,cir b){
    34     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    35 }
    36 db f(db x){
    37     db ret=0,ls=-inf;
    38     int top=0;
    39     for(int i=1;i<=n;i++){
    40         p[++top]=c[i].getf(x);
    41         if(p[top].x==0&&p[top].y==0)top--;
    42     }
    43     sort(p+1,p+top+1);
    44     for(int i=1;i<=top;i++){
    45         if(p[i].x>ls){
    46             ret+=p[i].y-p[i].x;
    47             ls=p[i].y;
    48         }else if(p[i].y>ls){
    49             ret+=p[i].y-ls;
    50             ls=p[i].y;
    51         }
    52     }
    53     return ret;
    54 }
    55 db sim(db l,db r,db fl,db fm,db fr){
    56     return (fl+4*fm+fr)*(r-l)/6;
    57 }
    58 db sint(db l,db r,db fl,db fm,db fr){
    59     db mid=(l+r)/2,L=f((l+mid)/2),R=f((mid+r)/2),s=sim(l,r,fl,fm,fr),sl=sim(l,mid,fl,L,fm),sr=sim(mid,r,fm,R,fr);
    60     if(fabs(sl+sr-s)<eps)return sl+sr;
    61     return sint(l,mid,fl,L,fm)+sint(mid,r,fm,R,fr);
    62 }
    63 int main(){
    64     scanf("%d",&n);
    65     for(int i=1;i<=n;i++){
    66         scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].r);
    67         mi=min(mi,c[i].x-c[i].r);
    68         mx=max(mx,c[i].x+c[i].r);
    69     }
    70     sort(c+1,c+n+1);
    71     for(int i=1;i<=n;i++){
    72         if(!c[i].ch){
    73             for(int j=i+1;j<=n;j++){
    74                 if(!c[j].ch&&dis(c[i],c[j])<=c[i].r-c[j].r){
    75                     c[j].ch=true;
    76                 }
    77             }
    78         }
    79     }
    80     for(int i=1;i<=n;i++){
    81         if(c[i].ch)swap(c[i--],c[n--]);
    82     }
    83     printf("%.3lf",sint(mi,mx,0,f((mi+mx)/2),0));
    84     return 0;
    85 }
  • 相关阅读:
    MTK 官方 openwrt SDK 使用
    PF_RING packet overwrites
    pycares cffi
    libevent evbuffer bug
    浮点转字符串性能比较
    重写 libev 的 EV_WIN32_HANDLE_TO_FD
    thrift TNonblockingServer 使用
    accel-pptp 部署
    boost::asio 使用 libcurl
    蜂鸟A20开发板刷 cubietruck 的 SD 卡固件
  • 原文地址:https://www.cnblogs.com/dcdcbigbig/p/10056681.html
Copyright © 2011-2022 走看看