自适应辛普森法
- 参考
- 引入
- ⌈ \lceil ⌈辛普森法求积分 ⌋ \rfloor ⌋
-
- ⌈ \lceil ⌈自适应辛普森法求积分 ⌋ \rfloor ⌋
-
- 原理
- 优点
- 缺点
- 代码
- 例题
-
- 模板:洛谷 P4526
- 椭圆:HDU1724
- Youmu with Master spark:QLU contest 003
参考
- [学习笔记]自适应辛普森(Simpson)积分 [ 1 ] [1] [1]
引入
【模板】自适应辛普森法2 | 洛谷 P4526
- 给定 a a a 为小于等于 50 50 50 的实数,求
∫ 0 ∞ x a x − x d x \int_0^\infin x^{\frac{a}{x}-x}dx ∫0∞xxa−xdx
若发散,输出 o r z orz orz。
若收敛,保留 5 5 5 位小数。
⌈ \lceil ⌈辛普森法求积分 ⌋ \rfloor ⌋
原理
- 对于 ∫ a b f ( x ) d x \int_a^bf(x)dx ∫abf(x)dx,如果这个 f ( x ) f(x) f(x) 我们很难对其进行积分计算的话
我们可以用二次函数拟合,即设 f ( x ) ≈ ( A x 2 + B x + C ) f(x)\approx(Ax^2+Bx+C) f(x)≈(Ax2+Bx+C)
然后经过推导 [ 1 ] [1] [1] ,得到一下结果:
∫ a b f ( x ) d x ≈ ( b − a ) ( f ( a ) + 4 f ( a + b 2 ) + f ( b ) ) 6 \int_a^b f(x)dx\approx\cfrac{(b-a)(f(a)+4f(\frac{a+b}{2})+f(b))}{6} ∫abf(x)dx≈6(b−a)(f(a)+4f(2a+b)+f(b))
其中 b − a b-a b−a 的值越小,值越精确。
思考
- 但是,原函数 f ( x ) f(x) f(x) 不一定能用一个二次函数来得到很好的拟合。
我们想,能否 f ( x ) f(x) f(x) 分成一段一段的,每一段都可以很好地用二次函数来拟合?
貌似可行,但是怎么分段?这就引出了自适应辛普森法。
⌈ \lceil ⌈自适应辛普森法求积分 ⌋ \rfloor ⌋
原理
- 一个很自然的想法是,利用分治去处理区间。
(1)分治 a s r ( l , r , e p s , a n s ) asr(l,r,eps,ans) asr(l,r,eps,ans)
l , r l,r l,r 表示这个分治的区间的坐标
e p s eps eps 表示一个期望令人满意的精度
a n s ans ans 存的是利用辛普森法算出的该区域的近似结果
(2)进行分治计算。
算出中点坐标 m i d = l + r 2 mid=\frac{l+r}{2} mid=2l+r
用辛普森法计算
左半区域的近似和 l s u m = s i m p ( l , m i d ) lsum=simp(l,mid) lsum=simp(l,mid) 和右半区域的近似和 r s u m = s i m p ( m i d , r ) rsum=simp(mid,r) rsum=simp(mid,r)
如果该区域的近似和与左半区域的近似和加上右半区域的近似和差不多,那么我们返回 a n s ans ans。
否则,继续左递归和右递归。
- 一些奇怪的优化
根据历来 d a l a o dalao dalao 的优化,该区域精度需要有一个奇妙的判断,但这不影响我们的理解。
为什么递归条件为精度*15
优点
- 可以计算某个函数的定积分,不需要考虑到如何去积分。
该函数只要平滑,都可以带入去运算。
一些难以运算但可以通过近似去解的函数
缺点
- 首先,因为是实数运算,精度 和传入 e p s eps eps 挂钩。如果精度高了,运算时间 难免会增加。
其次,要求函数较平滑,否则二次函数拟合程度较差。
还有,要求积分上下限不能太大,更不能积到无穷去。时间和精度都不允许。
- 总结:精度差,时间高,函数要求平滑,上下限不能差太大。
代码
double func(double x){
return sin(x);
}
double simp(double l,double r){
double mid = (l+r)/2.0;
return (func(l) + 4.0*func(mid) + func(r)) * (r - l) / 6.0;
}
double asr(double l,double r,double eps,double ans){
double mid = (l+r)/2.0;
double ls = simp(l,mid),rs = simp(mid,r);
if(fabs(ls + rs - ans) < 15 * eps)return ls + rs + (ls + rs - ans) / 15;
return asr(l,mid,eps/2.0,ls) + asr(mid,r,eps/2.0,rs);
}
例题
模板:洛谷 P4526
∫ 0 ∞ x a x − x d x \int_0^\infin x^{\frac{a}{x}-x}dx ∫0∞xxa−xdx
- 什么时候发散呢? a < 0 a<0 a<0发散。(打表也可以知道)
- 积分上下限怎么选择?
(1)积分下限不能取 0 0 0 ,因为该点计算无意义,下限取 e p s eps eps
(2)积分上限:正无穷?做不了呀?画图:

可以看到,最猛的 a = 50 a=50 a=50,也在 x > 10 x>10 x>10 出很快趋于 0 0 0了。
我们积分上限取 15 15 15 即可。
double a;
double func(double x){
return pow(x,a/x-x);
}
double simp(double l,double r){
double mid = (l+r)/2;
return (func(l) + 4*func(mid) + func(r)) * (r - l) / 6;
}
double asr(double l,double r,double eps,double ans){
double mid = (l+r)/2;
double ls = simp(l,mid),rs = simp(mid,r);
if(fabs(ls + rs - ans) < 15 * eps)return ls + rs + (ls + rs - ans) / 15;
return asr(l,mid,eps/2,ls) + asr(mid,r,eps/2,rs);
}
int main()
{
cin >> a ;
if(a < 0){
puts("orz");
return 0;
}
cout << fixed << setprecision(5) << asr(EPS,15,EPS,0);
return 0;
}
椭圆:HDU1724
- Ellipse | HDU1724
给定椭圆曲线 x 2 a 2 + y 2 b 2 = 1 \cfrac{x^2}{a^2}+\cfrac{y^2}{b^2}=1 a2x2+b2y2=1
给定两根直线 x = L x=L x=L 和 x = R x=R x=R
求两条直线之内,和椭圆所围出的面积。
- 算出 y = f ( x ) y=f(x) y=f(x) 即可。 f ( x ) = b 1 − x 2 a 2 f(x)=b\sqrt{1-\cfrac{x^2}{a^2}} f(x)=b1−a2x2
然后带入即可。
double a,b,l,r;
double func(double x){
return sqrt(1.0-x*x/a/a)*b;
}
double simp(double l,double r){
double mid = (l+r)/2.0;
return (func(l) + 4.0*func(mid) + func(r)) * (r - l) / 6.0;
}
double asr(double l,double r,double eps,double ans){
double mid = (l+r)/2.0;
double ls = simp(l,mid),rs = simp(mid,r);
if(fabs(ls + rs - ans) < 15 * eps)return ls + rs + (ls + rs - ans) / 15;
return asr(l,mid,eps/2.0,ls) + asr(mid,r,eps/2.0,rs);
}
int main()
{
int T;scanf("%d",&T);
while(T--){
scanf("%lf%lf%lf%lf",&a,&b,&l,&r);
printf("%.3f\n",asr(l,r,EPS,0)*2);
}
return 0;
}
Youmu with Master spark:QLU contest 003
- 二月八号新鲜的比赛,也是这题让我了解到了自适应辛普森积分。
但是这题出题人事先不知道辛普森积分做法 ???
- 【题意】Youmu with Master spark
给定一个积分函数
f ( x ) = ∫ 0 1 t 2 ∣ e 2 t − x ∣ d t f(x)=\int_0^1t^2|e^{2t}-x|dt f(x)=∫01t2∣e2t−x∣dt
求
∑ i = 1 n f ( i ) \sum_{i=1}^n f(i) i=1∑nf(i)
- 【数据范围】
样例组数 T ≤ 1 0 5 T\le 10^5 T≤105
1 ≤ n ≤ 1 0 9 1\le n\le 10^9 1≤n≤109
精度误差要求 ≤ 1 0 − 8 \le 10^{-8} ≤10−8 (但是根据评测机显示不需要精度这么高?)
- 【思路】
看一下那个绝对值,当 x ≥ e 2 t x\ge e^{2t} x≥e2t 恒成立的话貌似可以直接去掉了,下限最大值为 e 2 e^2 e2
也就是说,如果 x ≤ 7 x\le 7 x≤7,函数比较复杂,我们用自适应辛普森法去算出每一个 f ( i ) f(i) f(i) 来。
如果 x ≥ 8 x\ge 8 x≥8,我们可以把函数进行化简:
f ( x ≥ 8 ) = ∫ 0 1 t 2 ( x − e 2 t ) d t = x ∫ 0 1 t 2 d t − ∫ 0 1 t 2 e 2 t d t = x 3 − 1 2 ∫ 0 1 t 2 e 2 t d ( 2 t ) = x 3 − 1 2 ∫ 0 2 ( y 2 ) 2 e y d y = x 3 − 1 8 ∫ 0 2 y 2 d ( e y ) = x 3 − 1 8 ( y 2 e y ∣ 0 2 − ∫ 0 2 e y d ( y 2 ) ) = x 3 − 1 8 ( 4 e 2 − 2 ∫ 0 2 y d ( e y ) ) = x 3 − 1 8 ( 4 e 2 − 2 ( y e y ∣ 0 2 − ∫ 0 2 e y d y ) ) = x 3 − 1 8 ( 4 e 2 − 2 ( 2 e 2 − e y ∣ 0 2 ) ) = x 3 − 1 8 ( 4 e 2 − 2 ( 2 e 2 − e 2 + 1 ) ) = x 3 − 1 8 ( 2 e 2 − 2 ) = x 3 − e 2 − 1 4 \begin{aligned} f(x\ge8)&=\int_0^1t^2(x-e^{2t})dt\\ &=x\int_0^1t^2dt-\int_0^1t^2e^{2t}dt\\ &=\frac{x}{3}-\frac{1}{2}\int_0^1t^2e^{2t}d(2t)\\ &=\frac{x}{3}-\frac{1}{2}\int_0^2(\frac{y}{2})^2e^ydy\\ &=\frac{x}{3}-\frac{1}{8}\int_0^2y^2d(e^y)\\ &=\frac{x}{3}-\frac{1}{8}\Big(y^2e^y\Big|_0^2-\int_0^2 e^yd(y^2)\Big)\\ &=\frac{x}{3}-\frac{1}{8}\Big(4e^2-2\int_0^2yd(e^y)\Big)\\ &=\frac{x}{3}-\frac{1}{8}\Big(4e^2-2(ye^y\Big|_0^2-\int_0^2e^ydy)\Big)\\ &=\frac{x}{3}-\frac{1}{8}\Big(4e^2-2(2e^2-e^y\Big|_0^2)\Big)\\ &=\frac{x}{3}-\frac{1}{8}\Big(4e^2-2(2e^2-e^2+1)\Big)\\ &=\frac{x}{3}-\frac{1}{8}(2e^2-2)\\ &=\frac{x}{3}-\frac{e^2-1}{4} \end{aligned} f(x≥8)=∫01t2(x−e2t)dt=x∫01t2dt−∫01t2e2tdt=3x−21∫01t2e2td(2t)=3x−21∫02(2y)2eydy=3x−81∫02y2d(ey)=3x−81(y2ey∣∣∣02−∫02eyd(y2))=3x−81(4e2−2∫02yd(ey))=3x−81(4e2−2(yey∣∣∣02−∫02eydy))=3x−81(4e2−2(2e2−ey∣∣∣02))=3x−81(4e2−2(2e2−e2+1))=3x−81(2e2−2)=3x−4e2−1
好家伙!主要是要用一次变量替换和两次分部积分,复习了一下。
那么, ∀ x ≥ 8 \forall x\ge 8 ∀x≥8, f ( x ) = x 3 − e 2 − 1 4 f(x)=\frac{x}{3}-\frac{e^2-1}{4} f(x)=3x−4e2−1,我们简单做一下合并:
A n s ( n ≥ 8 ) = ∑ i = 1 7 f ( i ) + ∑ i = 8 n f ( i ) = ∑ i = 1 7 f ( i ) + ∑ i = 8 n ( x 3 − e 2 − 1 4 ) Ans(n\ge8)=\sum_{i=1}^7f(i)+\sum_{i=8}^nf(i)=\sum_{i=1}^7f(i)+\sum_{i=8}^n\Big(\frac{x}{3}-\frac{e^2-1}{4}\Big) Ans(n≥8)=i=1∑7f(i)+i=8∑nf(i)=i=1∑7f(i)+i=8∑n(3x−4e2−1)
右边的东西明显可以化简嘛!化简就是最终答案了:
A n s ( n ≥ 8 ) = ∑ i = 1 7 f ( i ) + ( 8 + n ) ( n − 7 ) 2 × 3 + e 2 − 1 4 ( n − 7 ) Ans(n\ge 8)=\sum_{i=1}^7f(i)+\cfrac{(8+n)(n-7)}{2\times3}+\frac{e^2-1}{4}(n-7) Ans(n≥8)=i=1∑7f(i)+2×3(8+n)(n−7)+4e2−1(n−7)
double ans[10];
double func(double x){
return x*x*fabs(exp(2.0*x) - a);
}
double simp(double l,double r){
double mid = (l+r)/2.0;
return (func(l) + 4.0*func(mid) + func(r)) * (r - l) / 6.0;
}
double asr(double l,double r,double eps,double ans){
double mid = (l+r)/2.0;
double ls = simp(l,mid),rs = simp(mid,r);
if(fabs(ls + rs - ans) < 15 * eps)return ls + rs + (ls + rs - ans) / 15;
return asr(l,mid,eps/2.0,ls) + asr(mid,r,eps/2.0,rs);
}
int main()
{
for(int i = 1;i <= 7;++i){
a = i;
ans[i] = asr(0,1,EPS,0);
}
const double ex = (exp(2.0) - 1)/4.0;
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
double res = 0;
for(int i = 1;i <= min(n,7);++i)
res += ans[i];
if(n > 7){
res += 1.0*(8.0+n)*(n-7.0)/6.0;
res -= ex * (n - 7);
}
printf("%.10f\n",res);
}
return 0;
}