簡體   English   中英

C++中的復合辛普森規則

[英]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減去,而所有內部點的權重都切換了。 事實上,這是代碼計算的:

\\frac{\\Delta x}{3}\\left ( 3f(x_0)+ 2f(x_1) + 4f(x_2) + ... + 2f(x_{n-1}) + 3f(x_{n}) \\對 )

另外,使用

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM