G57 自适应辛普森积分
视频链接:https://www.bilibili.com/video/BV1mv4y1G78Q/
自适应辛普森法
- 任意可积函数分割成很多段,每段就可以用二次函数来拟合,套用辛普森公式进行近视计算。
- 对积分区间不断二分,每次判断当前段和二次函数的相似程度,如果足够相似就直接代入辛普森公式计算,否则将当前段分割成左右两段递归求解。
- 如何判断当前段和二次函数是否相似? 把当前段直接代入公式求积分,再将当前段从中点分割成两段,把这两段再直接代入公式求积分。如果当前段的积分和分割成两段后的积分之和相差很小,就可以认为当前段和二次函数很相似了,不用再递归分割了。
分割递归次数与区间宽度 n、精度 eps 有关
时间复杂度:𝑂(𝑙𝑜𝑔(𝑛/𝑒𝑝𝑠))
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const double eps=1e-10; double a,b,c,d,l,r; double f(double x){ //积分函数 return (c*x+d)/(a*x+b); } double simpson(double l,double r){//辛普森公式 return (r-l)*(f(l)+f(r)+4*f((l+r)/2))/6; } double asr(double l,double r,double ans){//自适应 auto m=(l+r)/2,a=simpson(l,m),b=simpson(m,r); if(fabs(a+b-ans)<eps) return ans; return asr(l,m,a)+asr(m,r,b); } int main(){ scanf("%lf%lf%lf%lf%lf%lf",&a,&b,&c,&d,&l,&r); printf("%.6lf",asr(l,r,simpson(l,r))); return 0; }
估算一下积分上限
条件:𝑎=50,eps=10^(−10)
𝑥=10 时,f(𝑥)=10^(5−10)=10^(−5)>eps,
𝑥=20 时,f(𝑥)=20^(2.5−20)=20^(−17.5)<eps,
所以 [0,+∞] 转化为 [eps,20]
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const double eps=1e-10; double a,l,r; inline double f(double x){ //积分函数 return pow(x,(a/x)-x); } double simpson(double l,double r){//辛普森公式 return (r-l)*(f(l)+f(r)+4*f((l+r)/2))/6; } double asr(double l,double r,double ans){//自适应 auto m=(l+r)/2,a=simpson(l,m),b=simpson(m,r); if(fabs(a+b-ans)<eps) return ans; return asr(l,m,a)+asr(m,r,b); } int main(){ scanf("%lf",&a); if(a<0) return printf("orz"),0; printf("%.5lf",asr(eps,20,simpson(l,r))); return 0; }
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const double eps=1e-10; double a,b,l,r; inline double f(double x){ //积分函数 return b*sqrt(1-(x*x)/(a*a)); } double simpson(double l,double r){//辛普森公式 return (r-l)*(f(l)+f(r)+4*f((l+r)/2))/6; } double asr(double l,double r,double ans){//自适应 auto m=(l+r)/2,a=simpson(l,m),b=simpson(m,r); if(fabs(a+b-ans)<eps) return ans; return asr(l,m,a)+asr(m,r,b); } int main(){ int t; scanf("%d",&t); while(t--){ scanf("%lf%lf%lf%lf",&a,&b,&l,&r); printf("%.3lf\n",2*asr(l,r,simpson(l,r))); } }
练习: