Up

ガウス ルジャンドル積分公式の係数の計算

long int、関数オブジェクト、テンプレート、例外など

 

通常は、double 型を用いていても、公式の係数を求めるときはdouble 型より1つ精度の高いlong int 型で計算を行って求め、それをdouble 型の計算で用いると精度がより安心である。定数としてdouble 型の精度を超える桁数の数値を書いても、コンパイラーによって四捨五入されてdouble 型に変換される。係数を求める例として、ガウス・ルジャンドルの積分公式における係数を求めるプログラムを作成してみた。分点が n 個である積分公式の分点は、次式の根として与えられる(岡本安晴「Delphiで学ぶデータ分析法」CQ出版社、1998pp. 236-238)。

分点の重みは次式によって与えられる。

上式によりの場合のプログラムを作成した。関数オブジェクト、テンプレート、例外の生成(下記のプログラムでは、例外は生成されないが)も使用してみた。プログラムは圧縮ファイルSource.zipとしてまとめたので、圧縮ファイル名Source.zipをクリックダウンロード解凍すればエディタで開くことができる。プログラムをFedora23で実行すると以下のようになる。

 

Source.cppの内容は以下のとおりである。

 

#include <iostream>

#include <iomanip>

#include <sstream>

#include <cmath>

 

using namespace std;

 

inline long double sqr( long double v ){

        return v * v;

}

 

class CP {

public:

        int n;

        CP(){}

        CP(int vn): n(vn){}

        ~CP(){}

 

        void set_n(int vn) {

                n = vn;

        }

 

        long double pfunc(long double x, int k) {

                if (k > 1) {

                        return ((2.0 * k - 1.0) * x * pfunc(x, k - 1)

                                - (k - 1.0) * pfunc(x, k - 2)) / k;

                }

                else if (k == 1) {

                        return x;

                }

                else if (k == 0) {

                        return 1.0;

                }else{

                        ostringstream sout;

                        sout << "Error at pfunc...k = " << k;

                        throw sout.str();

                }

        }

 

        long double operator()(long double x) {

                return pfunc(x, n);

        }

};

 

class CDP {

public:

        int n;

        CDP() {}

        CDP(int vn) : n(vn) {}

        ~CDP() {}

 

        void set_n(int vn) {

                n = vn;

        }

 

        long double pfunc(long double x, int k) {

                if (k > 1) {

                        return ((2.0 * k - 1.0) * x * pfunc(x, k - 1)

                                - (k - 1.0) * pfunc(x, k - 2)) / k;

                }

                else if (k == 1) {

                        return x;

                }

                else if (k == 0) {

                        return 1.0;

                }

                else {

                        ostringstream sout;

                        sout << "Error at pfunc...k = " << k;

                        throw sout.str();

                }

        }

 

        long double dpfunc(long double x, int k) {

                if (k > 1) {

                        return ((2.0 * k - 1.0)  * (pfunc(x, k - 1) + x * dpfunc(x, k - 1))

                                - (k - 1) * dpfunc(x, k - 2)) / k;

                }

                else if (k == 1) {

                        return 1.0;

                }

                else if (k == 0) {

                        return 0.0;

                }

                else {

                        ostringstream sout;

                        sout << "Error at dpfunc...k = " << k;

                        throw sout.str();

                }

        }

 

        long double operator()(long double x) {

                return dpfunc(x, n);

        }

};

 

 

//     Bisection法による根の計算   

template<class TF>

void TBisection(TF & f,                                 //   関数

                long double s,                          //   fが増加関数のとき s > 0.0

                long double c,                          //   根に対する関数値

                long double L_b, long double U_b,       //   根の存在区間の下限と上限

                long double & Root,                     //     f(Root) = c

                long double acc)                        //   精度

{

        if ((f(L_b) - c) * (f(U_b) - c) > 0.0) {

                ostringstream sout;

                sout << "Invalid combination of L_b and U_b at TBisection.";

                throw sout.str();

        }

        do {

                long double m = 0.5 * (L_b + U_b);

                long double v = f(m);

                if (s * (v - c) > 0.0) {

                        U_b = m;

                }

                else {

                        L_b = m;

                }

        } while (abs(U_b - L_b) >= acc);

 

        Root = 0.5 * (L_b + U_b);

}

 

//     Newton法による根の計算  

template<class TF, class TDF>

void TNewton(   long double & x,        //  根の初期値、戻り値は根 f(x)=c

                 long double c,         //  関数値

                 TF & f,                //  関数

                 TDF & df,              //  導関数

                 long double acc)       //      精度

{

        int NStep = 0;

        int ck = 0;

        long double x0;

        do {

                x0 = x;

                x = x0 + (c - f(x0)) / df(x0);

        } while (abs(x - x0) >= acc);  

        return;

}

 

int main() {

        try {

                CP p(9);

                CDP dp(9);

                long double xpos0 = -1.0 + 0.01;

                cout << setprecision(19) << scientific;

                for (int istep = 0; istep < 9; istep++) {

                        cout << "istep = " << istep << endl;

                        long double ck_v = p(xpos0);

                        long double xpos1 = xpos0;

                        long double v;

                        do {

                                xpos1 += 0.01;

                                v = p(xpos1);

                                if (v * ck_v < 0.0) break;

                        } while (true);

                        long double xi;

                        TBisection<CP>(p, v - ck_v,     0.0, xpos0, xpos1, xi, 1e-7);                  

               

                        TNewton<CP, CDP>( xi, 0.0, p, dp, 1.0e-19);    

                        cout << "xi = " << setw(30) << left << xi;

 

                        long double wi = 2.0 / ((1.0 - sqr(xi)) * sqr(dp(xi)));

                        cout << "wi = " << wi << endl;

 

                        xpos0 = xi + 0.01;

 

                }

        }

        catch (string s){

                        cout << s << endl;

        }

        return 0;

}

 

 

Up