/* * nint6.c --- DE 公式 * * 1 2 (1/2) * I1 = ∫ (1-x ) dx = π/2 * -1 * * 1 2 (-1/2) * I2 = ∫ (1-x ) dx = π * -1 * * いずれも端点に特異性があって、古典的な数値積分公式はうまく行かない。 * double exponential formula (DE公式) ならばうまく計算できる。 * * コンパイル: gcc -o nint6 nint6.c -lm * * 学生 (横山和正君) の指摘により少し修正を加える (2004/1/15)。 */ #include <stdio.h> #include <math.h> typedef double rrfunction(double); double debug = 0; double pi, halfpi; /* φ */ double phi(double t) { return tanh(halfpi * sinh(t)); } /* 2乗 */ double sqr(double x) { return x * x; } /* φ' */ double dphi(double t) { return halfpi * cosh(t) / sqr(cosh(halfpi * sinh(t))); } /* DE 公式による (-1,1) における定積分の計算 */ double de(rrfunction f, double h, double N) { int n; double t, S, dS; S = f(phi(0.0)) * dphi(0.0); for (n = 1; n <= N; n++) { t = n * h; dS = f(phi(t)) * dphi(t) + f(phi(-t)) * dphi(- t); S += dS; if (fabs(dS) < 1.0e-16) { if (debug) printf("\tde(): n=%d, |t|=%g, fabs(dS)=%g\n", n, t, fabs(dS)); break; } } return h * S; } /* テスト用の被積分関数 その1 */ double f1(double x) { return sqrt(1 - x * x); } /* テスト用の被積分関数 その2 */ double f2(double x) { if (x >= 1.0 || x <= -1.0) return 0; else return 1 / sqrt(1 - x * x); } void test(rrfunction f, double exact) { int m, N; double h, IhN; /* |t|≦10 まで計算することにする */ h = 1.0; N = 10; /* h を半分, N を倍にして double exponential formula で計算してゆく */ for (m = 1; m <= 10; m++) { IhN = de(f, h, N); printf("h=%f, I_h=%25.16f, I_h-I=%e\n", h, IhN, IhN - exact); h /= 2; N *= 2; } } int main(int argc, char **argv) { if (argc >= 2 && strcmp(argv[1], "-d") == 0) debug = 1; pi = 4.0 * atan(1.0); halfpi = pi / 2; printf("test1 (sqrt(1-x^2) の積分)\n"); test(f1, halfpi); printf("test2 (1/sqrt(1-x^2) の積分)\n"); test(f2, pi); return 0; }
桂田 祐史