一、辛普森公式(二次函數積分公式):
用途:求一個積分的近似值
拓展用途:由於積分的幾何意義是函數圖像和x軸所圍成的圖形的面積,因此常用於在計算幾何中計算面積
tips1:這里的f(x)可以是任意一個函數。
tips2:但自適應辛普森法只能用於定義域連續不中斷的函數(注意是定義域連續不中斷即可,函數圖像變化不連續不會產生任何影響)。
tips3:求得的結果是近似值而非准確值,因此在某些有精度要求的情況下需要進行分治思想下的精度優化
二、普通辛普森法:、
代碼實現:
const int N = 1000 * 1000;
double simpson(double a, double b)
{
double h = (b - a) / N;
double s = f(a) + f(b);
for (int i = 1; i <= N - 1; ++i)
{
double x = a + h * i;
s += f(x) * ((i & 1) ? 4 : 2);
}
s *= h / 3;
return s;
}
特點:就是優化了辛普森公式的精度,但是精度仍然略有不足
用途:用途與辛普森公式完全相同
三、自適應辛普森法:
算法的核心思想
由於精度和所用時間互相影響,因此我們需要對於實際問題所需選擇適當的計算精度eps,同時保證精度和時間復雜度。
核心思想的轉化
精度的核心是分段的方式。因此關鍵就成為了如何分合適的段數、合適的段的大小。
將問題進一步轉化
由於基礎的辛普森公式是二次函數積分的近似求法,我們就可以考慮當前要求的f(x)和二次函數的相似程度,以此決定是否要繼續分段。
我們這樣考慮:假如有一段圖像已經很接近二次函數的話,直接帶入公式求積分,得到的值精度就很高了,不需要再繼續分割這一段了。
於是我們有了這樣一種分割方法:每次判斷當前段和二次函數的相似程度,如果足夠相似的話就直接代入公式計算,否則將當前段分割成左右兩段遞歸求解。
實現思路
現在問題就變成了:如何判斷某一段圖像和二次函數很接近
我們把當前段直接代入公式求積分,再將當前段從中點分割成兩段,把這兩段再直接代入公式求積分。如果當前段的積分和分割成兩段后的積分之和相差很小的話,就可以認為當前段和二次函數很相似了,不用再遞歸分割了。
還有一些奇怪的細節
在分治判斷的時候,除了判斷精度是否正確,一般還要強制執行最少的迭代次數。
const double eps=只要合適設成多少都可以
double simpson(double l, double r)
{
double mid = (l + r) / 2;
return (r - l) * (f(l) + 4 * f(mid + f(r)) / 6; // 辛普森公式
}
double asr(double l, double r, double eqs, double ans, int step)
{
double mid = (l + r) / 2;
double fl = simpson(l, mid), fr = simpson(mid, r);
if(abs(fl + fr - ans) <= 15 * eqs && step < 0)
return fl + fr + (fl + fr - ans) / 15; // 足夠相似的話就直接返回
return asr(l, mid, eqs / 2, fl, step - 1) + asr(mid, r, eqs / 2, fr, step - 1); // 否則分割成兩段遞歸求解
}
double calc(double l, double r, double eps)
{
return asr(l, r, eps, simpson(l, r), 12);
}
代碼中“15”這個常數在同等的計算量下可以使結果的精度最優,這一點用數學歸納法挨個嘗試各種數據證明即可。
總結
整個過程,是有一個基礎的精度不高的公式,一直推出精度和時間復雜度都還算優秀的自適應辛普森法。
而這個算法一般用於計算幾何中求圖形面積(有一些不會正解的題可以用這個算法水暴力分)。
在二維和三維的計算幾何中可以用多次這個算法,來求體積或球體表面積(面積的積分是體積)
例題
洛谷自適應辛普森法模版1