概述
最近matlab中存在个二重积分的函数,携程c++的就给我整懵了,因为方法比较多,很容易结果对不上,之前的数值积分是用的龙贝格积分法,应该是前十位相同。这回辛普森的精确到9位,大概是没啥大问题。
辛普森积分法利用拉格朗日插值法来近似拟合原函数,然后通过对拟合函数的积分来简化近似。
近似公式如下:
$$ \int_{a}^{b} f(x) \mathrm{d} x \approx \frac{b-a}{6}\left(f(a)+4 f\left(\frac{a+b}{2}\right)+f(b)\right. $$
拉格朗日插值
拉格朗日插值利用二次曲线(抛物线)来拟合。
在求通过点集$(x_{1},y_{1})$,$(x_{2},y_{2})$,$(x_{3},y_{3})$,···,$(x_{n},y_{n})$的曲线上,拉格朗日利用多条二次曲线来拟合。首先二次曲线 $g = f(x)$ 满足
$$ f_{i}(x_{i}) \begin{cases} 1 \quad i = j\\ 0 \quad i \neq j\\ \end{cases} $$
因此$y_{n}f_{n}(x)$ 在$x_{n}$处,取值$y_{n}$,其余点取值为0。
拉格朗日通过对$f_{i}(x)$的构造如下:
$$ f_{i}(x)=\prod_{j=i}^{1 \leq j \leq n} \frac{\left(x-x_{j}\right)}{\left(x_{i}-x_{j}\right)} $$
最终得到了:
$$ f(x)=\sum_{i=1}^{n} y_{i}f_{i}(x) $$
辛普森公式
积分式:
$$ \int_{a}^{b} f(x)dx $$
第一我们需要用拉格朗日插值法对 $f(x)$ 进行近似计算,得出:
$$ f(x) \approx L_{n}(x) $$
$$ L_{n} = \sum_{i=1}^{n} y_{i}p_{i}(x) $$
$$ p_{i}(x)=\prod_{j=/i}^{1 \leq j \leq n} \frac{\left(x-x_{j}\right)}{\left(x_{i}-x_{j}\right)} $$
进而得出:
$$ f(x) \approx L_{n}(x) = \sum_{i=1}^{n} y_{i}p_{i}(x) $$
两边积分从而得出:
$$ \int_{a}^{b} f(x)dx \approx \int_{a}^{b} \sum_{i=1}^{n} y_{i}p_{i}(x) dx = \sum_{i=1}^{n} y_{i} \int_{a}^{b} p_{i}(x) dx $$
公式中的 $y_{i}=f(x_{i})& 。 令
$$ A_{i} = \int_{a}^{b} p_{i}(x) dx $$
那么就有:
$$ \int_{a}^{b} f(x)dx \approx \sum_{i=1}^{n} A_{i} f(x_{i}) $$
此外,如果在区间[a,b]中取n个点来拟合或者近似,则有 $h=\frac{b-a}{n}$,$X_{k}=a+kh$ , 所以:
$$ A_{i} = \int_{a}^{b} p_{i}(x) dx = h \int_{n}^{0} \prod_{j=1}^{n} \frac{(t-j)}{i-j}dt=(b-a)C_{a}^{(n)} $$
进而就有了:
$$ \int_{a}^{b} f(x)dx \approx \sum_{i=1}^{n} y_{i} A_{i} = (b-a)\sum_{i=0}^{n} C_{a}^{(n)} f(x_{i}) $$
该公式为牛顿-科特斯求积公式。
如果取3个点$x_{1}=a,x_{2}=\frac{a+b}{2},x_{3}=b$,则有下面:
$$ C_{0}^{(2)} = \frac{1}{6}, C_{1}^{(2)} = \frac{4}{6}, C_{2}^{(2)} = \frac{1}{6} $$
$$ \int_{a}^{b} f(x)dx \approx \frac{b-a}{6} (f(a)+4f(\frac{a+b}{2})+f(b)) $$
自适应辛普森积分
在辛普森公式中,三点的近似精度可能满足不了工程要求得精度,为此自适应辛普森积分会根据实际情况来自动调整精度。误差大的区域会划分多个区域。具体判断准则为:
$$ |S(a,c)+S(c,b)-S(a,b)| < 15 * eps $$
其中a , b为积分上下限,$c=\frac{a+b}{2}$,eps为精度
相关代码如下:
#include<iostream>
#include<stdlib.h>
using namespace std;
double Fun(double x){
return x*x+x;//积分函数,自定义函数
}
double simpson(double a, double b){
double c=(b+a)/2.0;
return (Fun(a)+4*Fun(c)+Fun(b))*(b-a)/6.0;//求辛普森近似值
}
double adsp(double a,double b, double eps, double S){//自适应辛普森递归过程
double c=(b+a)/2.0;
double L=simpson(a,c),R=simpson(c,b);
if(abs(L+R-S)<=15.0*eps){//判断准则
return L+R+(L+R-S)/15.0;
}
return adsp(a,c,eps/2.0,L)+adsp(c,b,eps/2.0,R);
}
double intergation(double a, double b, double eps){//求积分
return adsp(a,b,eps,simpson(a,b));
}
int main() {
cout<<intergation(0, 1, 0.00001)<<endl;
system("PAUSE");
return 0;
}
二重积分
二重积分公式:
$$ \int_{a}^{b}\int_{c}^{d}f(x,y)dx dy = \int_{a}^{b}F_{x}(y)dy \approx \frac{b-1}{6}(F_{x}(a)+4F_{x}(\frac{a+b}{2})+F_{x}(b)) $$
$$ F_{x}(y) = \int_{d}^{c}f(x,y)dx \approx \frac{d-c}{6}(f(c,y)+4f(\frac{d+c}{2},y)+f(d,y)) $$
二重积分只要根据辛普森公式固定y的值后,对f ( x , y ) f(x,y)f(x,y)求关于x的积分,之后再对y求积分就ok了。
相关代码如下:
CommonAlgorithm.h
/**
* @file CommonAlgorithm.h
* @author ybw (root@bug-maker.com)
* @brief 公共算法头文件
* @version 0.1
* @date 2022-01-20
*
* @copyright Copyright (c) 2022 CIOMP
*
*/
#ifndef _COMMON_ALGORITHM_H_
#define _COMMON_ALGORITHM_H_
#include <iostream>
#include <string>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
typedef double(*cbp)(double);
typedef double(*cbp2)(double,double);
namespace CommonAlgorithm {
/**
* @brief 二重积分函数(对matlab)
*
* @param cbp2 回调函数
* @param xa x上限
* @param xb x下限
* @param ya y上线
* @param yb y下限
* @param eps 步长
* @return double
*/
double integral2(double (* F)(double x, double y), double xa, double xb,double ya, double yb,double eps = 1.0e-8);
double simpsonX2(double (* F)(double x, double y), double a, double b,double y);
double adspX2(double (* F)(double x, double y), double a,double b,double y, double eps, double S);
double inte2(double (* F)(double x, double y), double a, double b,double y, double eps);
double simpsonY2(double (* F)(double x, double y), double xa, double xb,double ya, double yb,double eps);
double adspY2(double (* F)(double x, double y), double xa, double xb,double ya, double yb, double eps,double S);
double intergation2(double (* F)(double x, double y),double xa, double xb,double ya, double yb,double eps);
}
#endif
CommonAlgorithm.cpp
/**
* @file CommonAlgorithm.cpp
* @author ybw (root@bug-maker.com)
* @brief 公共算法源文件
* @version 0.1
* @date 2022-01-20
*
* @copyright Copyright (c) 2022 CIOMP
*
*/
#include "CommonAlgorithm.h"
double CommonAlgorithm::simpsonX2(double (* vFunction)(double x, double y), double a, double b,double y){//a,d为积分下、上限,y为被固定的y值
double c=(b+a)/2.0;
return (vFunction(a,y)+4*vFunction(c,y)+vFunction(b,y))*(b-a)/6.0;//对x的辛普森近似
}
double CommonAlgorithm::adspX2(double (* vFunction)(double x, double y), double a,double b,double y, double eps, double S){//对x的自适应辛普森递归
double c = (b + a) / 2.0;
double L = CommonAlgorithm::simpsonX2(vFunction,a,c,y),R=CommonAlgorithm::simpsonX2(vFunction,c,b,y);
if(abs(L+R-S)<=15.0*eps){
return L+R+(L+R-S)/15.0;
}
return CommonAlgorithm::adspX2(vFunction,a,c,y,eps/2.0,L)+CommonAlgorithm::adspX2(vFunction,c,b,y,eps/2.0,R);
}
double CommonAlgorithm::inte2(double (* vFunction)(double x, double y), double a, double b,double y, double eps){//固定y后,对x的积分
return CommonAlgorithm::adspX2(vFunction,a,b,y,eps,CommonAlgorithm::simpsonX2(vFunction,a,b,y));
}
double CommonAlgorithm::simpsonY2(double (* vFunction)(double x, double y), double xa, double xb,double ya, double yb,double eps){
double yc=(ya+yb)/2.0;
return (CommonAlgorithm::inte2(vFunction,xa,xb,ya,eps)+4*CommonAlgorithm::inte2(vFunction,xa,xb,yc,eps)+CommonAlgorithm::inte2(vFunction,xa,xb,yb,eps))*(yb-ya)/6.0;//对y的辛普森近似
}
double CommonAlgorithm::adspY2(double (* vFunction)(double x, double y), double xa, double xb,double ya, double yb, double eps,double S){//对y的自适应辛普森递归
double L=CommonAlgorithm::simpsonY2(vFunction,xa,xb,ya,(ya+yb)/2,eps);
double R=CommonAlgorithm::simpsonY2(vFunction,xa,xb,(ya+yb)/2,yb,eps);
if(abs(L+R-S)<=15.0*eps){
return L+R+(L+R-S)/15.0;
}
return CommonAlgorithm::adspY2(vFunction,xa,xb,ya,(ya+yb)/2.0,eps/2.0,L)+CommonAlgorithm::adspY2(vFunction,xa,xb,(ya+yb)/2.0,yb,eps/2.0,R);
}
double CommonAlgorithm::intergation2(double (* vFunction)(double x, double y),double xa, double xb,double ya, double yb,double eps){//求二重积分
return CommonAlgorithm::adspY2(vFunction,xa,xb,ya,yb,eps,CommonAlgorithm::simpsonY2(vFunction,xa,xb,ya,yb,eps));
}
double CommonAlgorithm::integral2(double (* vFunction)(double x, double y), double xa, double xb,double ya, double yb,double eps){
return CommonAlgorithm::adspY2(vFunction,xa,xb,ya,yb,eps,CommonAlgorithm::simpsonY2(vFunction,xa,xb,ya,yb,eps));
}