[英]Composite Simpson's Rule in C++
我一直在嘗試編寫一個函數來使用復合辛普森規則來近似積分的值。
template <typename func_type>
double simp_rule(double a, double b, int n, func_type f){
int i = 1; double area = 0;
double n2 = n;
double h = (b-a)/(n2-1), x=a;
while(i <= n){
area = area + f(x)*pow(2,i%2 + 1)*h/3;
x+=h;
i++;
}
area -= (f(a) * h/3);
area -= (f(b) * h/3);
return area;
}
我所做的是將函數的每個值乘以 2 或 4(和 h/3)與pow(2,i%2 + 1)
並減去邊緣,因為它們的權重僅為 1。
起初,我認為它工作得很好,但是,當我將它與我的梯形方法函數進行比較時,它更不准確,這不應該是這種情況。
這是我之前編寫的代碼的更簡單版本,它有同樣的問題,我認為如果我稍微清理一下,問題就會消失,但唉。 從另一篇文章中,我了解到類型和我對它們執行的操作會導致精度損失,但我只是沒有看到。
編輯:
為了完整起見,我正在為 e^x 從 1 到 0 運行它
\\function to be approximated
double f(double x){ double a = exp(x); return a; }
int main() {
int n = 11; //this method works best for odd values of n
double e = exp(1);
double exact = e-1; //value of integral of e^x from 0 to 1
cout << simp_rule(0,1,n,f) - exact;
辛普森法則使用這個近似值來估計一個定積分:
在哪里
和
這樣就有n + 1 個等距的樣本點x i 。
在發布的代碼中,傳遞給函數的參數n
似乎是對函數進行采樣的點數(而在前面的公式中n是間隔數,這不是問題)。
正確計算點之間的(恆定)距離
double h = (b - a) / (n - 1);
用於對所有點的加權貢獻求和的 while 循環從x = a
迭代到 ascissa 接近b
,但由於舍入誤差,可能不完全是b
。 這意味着f
的最后計算值f(x_n)
可能與預期的f(b)
略有不同。
但是,與以下事實所導致的錯誤相比,這算不了什么:這些端點在循環內以4的起始權重相加,然后在循環后以權重1減去,而所有內部點的權重都切換了。 事實上,這是代碼計算的:
另外,使用
pow(2, i%2 + 1)
生成序列 4, 2, 4, 2, ..., 4 就效率而言是一種浪費,並且可能會添加(取決於實現)其他不必要的舍入誤差。
以下算法顯示了如何在不調用該庫函數的情況下獲得相同(固定)的結果。
template <typename func_type>
double simpson_rule(double a, double b,
int n, // Number of intervals
func_type f)
{
double h = (b - a) / n;
// Internal sample points, there should be n - 1 of them
double sum_odds = 0.0;
for (int i = 1; i < n; i += 2)
{
sum_odds += f(a + i * h);
}
double sum_evens = 0.0;
for (int i = 2; i < n; i += 2)
{
sum_evens += f(a + i * h);
}
return (f(a) + f(b) + 2 * sum_evens + 4 * sum_odds) * h / 3;
}
請注意,此函數需要傳遞的間隔數(例如,使用 10 而不是 11 來獲得與 OP 函數相同的結果),而不是點數。
可在這里測試。
上述優秀且公認的解決方案可以受益於std::fma()
自由使用和浮點類型的模板化。 https://en.cppreference.com/w/cpp/numeric/math/fma
#include <cmath>
template <typename fptype, typename func_type>
double simpson_rule(fptype a, fptype b,
int n, // Number of intervals
func_type f)
{
fptype h = (b - a) / n;
// Internal sample points, there should be n - 1 of them
fptype sum_odds = 0.0;
for (int i = 1; i < n; i += 2)
{
sum_odds += f(std::fma(i,h,a));
}
fptype sum_evens = 0.0;
for (int i = 2; i < n; i += 2)
{
sum_evens += f(std::fma(i,h,a);
}
return (std::fma(2,sum_evens,f(a)) +
std::fma(4,sum_odds,f(b))) * h / 3;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.