辛普森积分法
1 定积分
在学习辛普森积分之前,我们需要了解一些基本的积分知识。
1.1 定义
设函数
记
其中
直观上来讲,
注意这里我们需要定义:
。 。
1.2 性质
定积分有如下基本性质:
-
线性性,即:
-
对区间有可加性,即:
-
保号性,即对于
,如果在区间 上 ,则:
1.3 牛顿-莱布尼茨公式
对于朴素的积分计算,我们有牛顿-莱布尼茨公式(又称微积分基本定理),是用于求定积分最常用且简单的方法。对于两个函数
上式右侧有时也记作
举个例子:求
我们知道
这就是关于定积分的基本知识,下面我们来看辛普森积分。
2 辛普森积分法
很多情况下,我们需要快速准确的求出一个积分的近似值。而辛普森积分法就是这样一种求数值积分的方法。
2.1 普通辛普森法
根据牛顿-莱布尼茨公式我们可以算出一些函数的积分值。但是有一些函数不一定有原函数
其中
而辛普森积分法实际上就是二阶牛顿-柯特斯闭型积分公式,也就是说我们用一个二次函数去适配原函数。众所周知,三个点可以确定一个二次函数,所以我们还需要在函数上找一个点,取
这就是所谓的辛普森积分法了。
下面我们证明一下辛普森积分法。设由
如此我们就可以得到原函数定积分的近似值。
2.2 自适应辛普森法
对于一个次数较高或者图像不接近二次函数的函数来讲,直接使用上面的辛普森法准确率显然很低。而自适应辛普森法就是用于解决这个问题的。
我们考虑对积分区间分段,如果我们能让分出来的每一段都接近二次函数,那么准确度就会大大提高。现在的问题是如何分段,如果段数过大会超时,如果段数过小准确度就不高。
考虑如下划分方法,我们先对当前区间
然后就是一些优化,在保证准确度的情况下减小运行时间:
-
考虑到有可能
的误差都很大,但是其差值恰好较小,此时我们依然认为其是准确的。我们考虑人为设定一个递归阈值,只有当递归层数超过该阈值的时候才会考虑直接退出。 -
为了防止小误差相加后得到的误差变大,我们在递归的时候需要将判定合法的误差阈值逐渐减小。
-
在求解的过程中,我们很有可能多次求函数值
,不妨利用记忆化减小运算常数。
不过上面的优化都是有概率负优化的,比如说模板题用优化 1 就不如不用,不过大部分时候全部加上是更好的。
2.3 代码
模板题:【模板】自适应辛普森法 1,代码如下:
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
double a, b, c, d, l, r;
unordered_map <double, double> mp;
double f(double x) {
if(mp.count(x)) return mp[x];//记忆化已经求过的函数值
return mp[x] = (c * x + d) / (a * x + b);
}
double simpson(double l, double r) {//普通辛普森积分法
double mid = (l + r) / 2;
return (f(l) + f(r) + 4 * f(mid)) * (r - l) / 6;
}
double asr(double l, double r, double ans, double eps, int stp) {
double mid = (l + r) / 2;
double fl = simpson(l, mid), fr = simpson(mid, r);//左右区间积分值
if(fabs(fl + fr - ans) <= 15 * eps/*误差不超过阈值*/ && stp < 0/*超过递归阈值*/) {
return fl + fr + (fl + fr - ans) / 15/*进一步修正误差*/;//返回答案
}
return asr(l, mid, fl, eps / 2, stp - 1) + asr(mid, r, fr, eps / 2, stp - 1);
//递归求解
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> a >> b >> c >> d >> l >> r;
double ans = asr(l, r, simpson(l, r), 1e-6, 5);
cout << fixed << setprecision(6) << ans << '\n';
return 0;
}
2.4 例题
实际上,辛普森积分在 OI 中的用途比较不大,除了暴力求积分以外,最常见的用途就是求图形面积并。
例 1 [CQOI2005] 三角形面积并
实际上此题可以直接扫描线,不过我们也可以用辛普森积分求解。
考虑设一个函数
那么我们直接上辛普森积分即可,问题就在于
接下来就是辛普森积分较扫描线的劣势了:它需要一定的卡精度技巧才能通过。对于此题而言,不仅要加入上面提到的优化,由于这个函数中很有可能有一大段都是
例 2 [NOI2005] 月下柠檬树
实际上这道题积分部分较为简单,难点在于前面的分析上。
首先投影是平行光的投影,所以圆的半径不会改变,只有两个圆之间的距离会改变。以柠檬树为原点建立平面直角坐标系,对于一个离地面高度为
不过显然这棵树的投影不止是这些分界上的圆,每个横截面上的圆也会构成阴影。考虑一个圆台的两个圆构成的阴影,则不难想像到该圆台中间任意横截面构成的投影正好填在了两圆的公切线之间,如下图所示:
圆
所以除了圆以外,我们还要求出两条切线的方程及定义域,也就是两个切点。由于图形一定关于
如上图所示。我们已知的有两圆半径(即
然后可以轻易解出
注意对于两圆半径大小关系进行分类讨论,尤其注意半径相等时不能利用上述方法求解,不过此时不难直接求出两切点坐标。
接下来进行辛普森积分,同样设
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律