自适应 Simpson 积分法,是一种计算一段区间内,形态奇怪的函数和的算法,例如面积并和难以直接用通项公式计算的函数。

Simpson 积分

我们都知道,求解微积分需要求解一个导数的原函数,但这显然更难,比如说 \(f(x)=\dfrac{cx+d}{ax+b}\) 这个函数,就相当难以求解原函数 \(F(x)\) (对应的例题是这个)。

假如我们在求定积分,我们考虑使用一个与 \(f(x)\) 函数图像相近的函数 \(f_n(x)=\sum\limits_{i=0}^na_ix^i\)。显然,\(F_n(x)\) 比较好求,为 \(\alpha+\sum\limits_{i=0}^na'_ix^{i+1}\)。那么我们就得到:

\[\int_a^bf(x)dx\cong\int_a^bf_n(x)dx=F_n(b)-F_n(a)
\]

为了方便运算,我们只考虑 \(n=2\) 的情况。相当于此时 \(f_2(x)\) 是一个二次函数。众所周知,要确定一个二次函数,需要确定其上的三个点。我们考虑选择 \(a,b,c=\dfrac{a+b}2\) 三点。我们设 \(f_2(x)=Ax^2+Bx+C\),则 \(F_2=\dfrac A3x^3+\dfrac B2x^2+Cx+\alpha\)。于是有:

\[\int_a^bf(x)dx\cong\int_a^bf_2(x)dx=F_2(b)-F_2(a)
\]
\[=\dfrac A3(b^3-a^3)+\dfrac B2(b^2-a^2)+C(b-a)
\]
\[=\frac{b-a}6(2A(b^2+ab+a^2)+3B(b+a)+6C)
\]
\[=\frac{b-a}6((Aa^2+Ba+C)+(Ab^2+Bb+C)+(A(a+b)^2+2B(a+b)+4C))
\]
\[=\frac{b-a}6(f(a)+f(b)+4(A(c)^2+B(c)+C))
\]
\[=\frac{b-a}6(f(a)+f(b)+4f(c))
\]

于是我们就得到了 Simpson 积分的一个公式:

\[\int_a^bf(x)dx\cong\frac{b-a}6(f(a)+f(b)+4f(c))
\]

自适应 Simpson 积分法

上面这个式子很不错,但是不够好,因为他太粗略了。

比如 \(\int_{-1}^1\frac{3x+4}{x+2}dx\),用 Simpson 积分算出来的值为 \(3.77\cdots\),而实际值已经超过 \(3.8\) 了。这就错的相当离谱。

其根本原因就在于,我们选择的 \(f_2(x)\) 不像 \(f(x)\)。但是我们还有一招:

\[\int_a^bf(x)dx=\int_a^cf(x)dx+\int_c^bf(x)dx
\]

为了方便,我们设 \(c=\frac{a+b}2\)。于是想到将原区间分成大量小区间,再用 Simpson 积分求出每个小区间的值,最后再全部合并起来。

比如上述问题,我们将问题转化为:

\[\int_{-1}^0\frac{3x+4}{x+2}dx+\int_0^1\frac{3x+4}{x+2}dx\cong 3.8
\]

假如肯再分解一层,就会得到 \(3.802549\cdots\),已经相当接近正确答案 \(3.802775\cdots\) 了。

但是显然,继续再分解下去一定会造成 \(TLE\),所以当务之急是确定递归的边界条件。

想法也很简单,假如满足:

\[|\int_a^bf_{a,b,2}(x)-(\int_a^cf_{a,c,2}(x)+\int_c^bf_{c,b,2}(x))|\le \alpha\times eps
\]

那么就停止递归,否则继续递归,同时 \(eps=\frac{eps}2\)。这里 \(\alpha\) 是一个常量,基本上可以说卡常专用了。

#include<bits/stdc++.h>
#define fsc(x) fixed<<setprecision(x)
using namespace std;
double a,b,c,d,L,R;
double f(double x){
return (c*x+d)/(a*x+b);
}double simpson(double l,double r){
double mid=(l+r)/2;
return (f(l)+f(mid)*4+f(r))*(r-l)/6;
}double dfs(double l,double r,double as,double eps){
double mid=(l+r)/2;
double a0=simpson(l,mid),a1=simpson(mid,r);
if(fabs(a0+a1-as)<=eps*15)
return a0+a1+(a0+a1-as)/15;
return dfs(l,mid,a0,eps/2)+dfs(mid,r,a1,eps/2);
}int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>a>>b>>c>>d>>L>>R;
cout<<fsc(6)<<dfs(L,R,simpson(L,R),1e-6);
return 0;
}

面积并

众所周知,有一道《计算几何水题》,他叫月下柠檬树。这里将以这道题讲解面积并问题如何转化为 Simpson 积分。


计算几何部分,考虑圆的投影还是一个等大的圆,只需要确定圆心就可以了。

圆台有两种情况。假如一个大圆完全包含另一个小圆,那么圆台的投影就是大圆;否则两圆必有两条公切线,连接后形成的图形即为圆台投影。

转化为公切线两端点,向各自圆心连线,加上公切线和两圆心连线所围成的图形(实为直角梯形)与两圆的面积并。

圆形部分好求,用相似三角形和勾股定理可以简单求出直角梯形的四个交点。

为减少时间复杂度,我们将圆形转为半圆,同时每个圆台只保留一个梯形,最终答案 \(\times 2\) 即可。


注:我求面积并的方法比较非主流,不喜勿喷。

那我们考虑设 \(f(x_0)\) 表示对于每个图形,和 \(x=x_0\) 的两个交点连成的线段的线段并。假如没有两个交点,那么我们就不管它。

有一个基本的思路是,面积并等于大量线段并之和,所以我们这样定义。

将梯形转化为更好求解的三角形,再进行求解即可。

#include<bits/stdc++.h>
#include<bits/extc++.h>
#define fsc(x) fixed<<setprecision(x)
#define SZ 5056577
using namespace std;
const int N=1505;
int n,m,k;double rd[N],o[N];
double al,sc[N][3][2],L,R;
struct hash_map{
struct data{double v;int u,nex;};
data e[SZ<<1];int h[SZ],cnt;
double& operator[](int u){
int hu=u%SZ;
for(int i=h[hu];i;i=e[i].nex) if(e[i].u==u) return e[i].v;
return e[++cnt]=(data){0.0,u,h[hu]},h[hu]=cnt,e[cnt].v;
}
}mp;struct line{double l,r;}ans[N];
int cmp(line x,line y){return x.l<y.l;}
double dts(double *a,double *b,double x){
return a[1]+(a[1]-b[1])/(a[0]-b[0])*(x-a[0]);
}double f(double x){
if(mp[(int)(x*500)]) return mp[(int)(x*500)];
for(int i=1;i<=n;i++){
if(x<o[i]-rd[i]||x>o[i]+rd[i]) continue;
ans[++k].l=0;
ans[k].r=sqrt(rd[i]*rd[i]-pow(x-o[i],2));
}for(int i=1;i<=m;i++){
if(x<sc[i][0][0]||x>sc[i][2][0]) continue;
ans[++k].l=dts(sc[i][0],sc[i][2],x);
ans[k].r=dts(sc[i][1],sc[i][2*(sc[i][1][0]<x)],x);
if(ans[k].l>ans[k].r) swap(ans[k].l,ans[k].r);
}sort(ans+1,ans+k+1,cmp);double re=0,lst=-1e9;
for(int i=1;i<=k;lst=max(lst,ans[i++].r))
re+=max(0.0,ans[i].r-max(lst,ans[i].l));
return k=0,mp[(int)(x*500)]=re*2;
}double simpson(double l,double r){
return (f(l)+4*f((l+r)/2)+f(r))*(r-l)/6;
}double dfs(double l,double r,double as,double eps){
double mid=(l+r)/2;
double a0=simpson(l,mid),a1=simpson(mid,r);
if(fabs(a0+a1-as)<=eps*15) return a0+a1+(a0+a1-as)/15;
return dfs(l,mid,a0,eps/2)+dfs(mid,r,a1,eps/2);
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>al,L=1e9,R=-1e9;
for(int i=1;i<=n+1;i++) cin>>o[i],o[i]+=o[i-1];
for(int i=1;i<=n;i++){
o[i]/=tan(al),cin>>rd[i];
L=min(o[i]-rd[i],L);
R=max(o[i]+rd[i],R);
}o[n+1]/=tan(al);
L=min(o[n+1],L),R=max(o[n+1],R);
for(int i=1;i<=n;i++){
if(o[i]+rd[i]>=o[i+1]+rd[i+1]) continue;
if(o[i]-rd[i]>=o[i+1]-rd[i+1]) continue;
sc[++m][1][0]=o[i+1],sc[m+1][0][0]=sc[m][0][0]=o[i];
sc[m+1][2][0]=sc[m][2][0]=o[i+1]+(rd[i]-rd[i+1])*rd[i+1]/(o[i+1]-o[i]);
sc[m+1][2][1]=sc[m][2][1]=sqrt(rd[i+1]*rd[i+1]-pow(sc[m][2][0]-o[i+1],2));
sc[++m][1][0]=o[i]+(rd[i]-rd[i+1])*rd[i]/(o[i+1]-o[i]);
sc[m][1][1]=sqrt(rd[i]*rd[i]-pow(sc[m][1][0]-o[i],2));
}for(int i=1;i<=m;i++){
if(sc[i][0][0]>sc[i][1][0])
swap(sc[i][0],sc[i][1]);
if(sc[i][1][0]>sc[i][2][0])
swap(sc[i][1],sc[i][2]);
if(sc[i][0][0]>sc[i][1][0])
swap(sc[i][0],sc[i][1]);
}cout<<fsc(2)<<dfs(L,R,simpson(L,R),2e-3);
return 0;
}

自适应 Simpson 积分法学习笔记的更多相关文章

  1. OpenGL ES学习笔记(二)——平滑着色、自适应宽高及三维图像生成

    首先申明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. <Android学习笔记--O ...

  2. X-Cart 学习笔记(二)X-Cart框架1

    目录 X-Cart 学习笔记(一)了解和安装X-Cart X-Cart 学习笔记(二)X-Cart框架1 X-Cart 学习笔记(三)X-Cart框架2 X-Cart 学习笔记(四)常见操作 四.X- ...

  3. 学习笔记:The Log(我所读过的最好的一篇分布式技术文章)

    前言 这是一篇学习笔记. 学习的材料来自Jay Kreps的一篇讲Log的博文. 原文很长,但是我坚持看完了,收获颇多,也深深为Jay哥的技术能力.架构能力和对于分布式系统的理解之深刻所折服.同时也因 ...

  4. OpenGL ES学习笔记(三)——纹理

    首先申明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. <OpenGL ES学习笔记( ...

  5. MIMO-OFDM通信系统学习笔记(一)

    [笔记一:单载波传输与多载波传输] MIMO-OFDM技术是3G-LTE,WiMAX通信系 统,以及WLan比如IEEE802.11a,IEEE802.11n等标准的关键技术,作为一枚通信狗,这些应该 ...

  6. FullCalendar 的学习笔记(一)

    前一段时间,一个老项目需要新增一个小功能,日程表~ 于是网上找了下,发现FullCalendar这个控件还不错于是就拿来用了下,下面简单介绍下我的学习笔记. 首先就是了解下FullCalendar的A ...

  7. AU3学习笔记

    目录 1. AU3是什么?能做什么? 2. 乱学AU3中的命令(语言相关)? 3. 通过简单示例学习AU3? 4. 正则表达式的学习(对大小写敏感) 5.对于GUI的相关学习 1.        AU ...

  8. TCP/IP详解学习笔记 这位仁兄写得太好了

      TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣 ...

  9. Deep Learning(深度学习)学习笔记整理系列之(八)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

  10. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

随机推荐

  1. arcpy获取polygon内环

    当使用arcpy获取polygon几何的时候,不能像ao一样获取到内外环,只能获取到单个部件.而part返回的即是一个点组了. 所以只能通过None对象进行分割,确定部件内的内外环.一个part内,只 ...

  2. 【Amadeus原创】Docker安装wikijs wiki系统

    拉取mysql8的镜像并运行 docker pull mysql docker run -d -v /data/mysql/data:/var/lib/mysql -v /data/mysql/con ...

  3. Python中指数概率分布函数的绘图详解

    在数据科学和统计学中,指数分布是一种应用广泛的连续概率分布,通常用于建模独立随机事件发生的时间间隔.通过Python,我们可以方便地计算和绘制指数分布的概率密度函数(PDF).本文将详细介绍指数分布的 ...

  4. .NET 阻止系统睡眠/息屏

    本文介绍Windows系统设备下如何阻止系统睡眠/息屏,以及想看当前阻止睡眠/息屏的应用信息 powercfg /requests查看活动列表 在播放音乐时,我们会发现设置了系统电源管理-自动睡眠,计 ...

  5. sed 删除 替换 文件内容

      sed添加一行内容 使用sed命令添加一行内容有多种实现方法,下面是几种不同的实现方法: 方法一:使用sed命令在指定行前添加一行内容     sed '2i This is a new line ...

  6. jQuery ajax - serializeArray() 方法 实例表单提交

    serializeArray()在ajax表单提交时候非常方便获取元素 定义和用法 serializeArray() 方法通过序列化表单值来创建对象数组(名称和值). 您可以选择一个或多个表单元素(比 ...

  7. 如何通过C#修改Windows操作系统时间

    C#的System.DateTime类提供了对日期时间的封装,用它进行时间的转换和处理很方便,但是我没有在其中找到任何可以用来修改系统时间的成员.用过VC.VB等的朋友可能知道,我们可以调用Win32 ...

  8. 百度高效研发实战训练营-Step4

    百度高效研发实战训练营-Step4 4.1 代码检测规则:Java案例详解 以Java的案例进行代码检查规则解释,代码检测规则可以分为以下十类: 4.1.1 源文件规范 该类规范,主要为从文件名.文件 ...

  9. 1. C++快速入门--变量和基本类型, 类别

    文章使用obsidian编写, 双链部分可能失效 1 基本内置类型 1.1 算术类型 算术类型介绍 bool 类型 字符类型 整数类型 实数浮点.虚数浮点和 复数浮点 参看如下表 带符号和无符号类型的 ...

  10. 自动化滑动极验v3示例

    import random import ddddocr from playwright.sync_api import sync_playwright import time import requ ...