(参考のため、かなり粗雑な書き方だが見せることにする。)
/*
* example6kai.c --- DE 公式
*
* 1
* I1 = ∫ (1-x^2)^{1/2}dx = π/2
* -1
*
* 1
* I2 = ∫ (1-x^2)^{-1/2} dx = π
* -1
*
* いずれも端点に特異性があって、古典的な数値積分公式はうまく行かない。
* double exponential formula (DE公式) ならばうまく計算できる。
*
* x=±1付近での情報落ちによる精度低下を防ぐため変数変換 (原点移動)
*
* コンパイル: cc -o example6kai example6kai.c
*
*/
#include <stdio.h>
#include <math.h>
#include <string.h>
typedef double ddfunction(double);
double pi, halfpi;
// tanh(t)-1 の計算 (t≫1の場合用)
double tanhm1(double t)
{
if (t <= 354)
return - 2.0 / (1.0 + exp(2.0 * t));
else {
printf("tahnm1(): %g は大きすぎる\n", t);
return 0;
}
}
// tanh(t)+1 の計算 (t≪-1の場合用)
double tanhp1(double t)
{
if (t >= - 354)
return 2.0 / (1.0 + exp(- 2.0 * t));
else {
printf("tahnp1(): %g は小さ過ぎる\n", t);
return 0;
}
}
/* φ */
double phi1(double t)
{
return tanh(halfpi * sinh(t));
}
/* ξ(t)=φ(t)-1 */
double xi(double t)
{
return tanhm1(halfpi * sinh(t));
}
/* η(t)=φ(t)+1 */
double eta(double t)
{
return tanhp1(halfpi * sinh(t));
}
/* 2乗 */
double sqr(double x) { return x * x; }
/* φ' */
double dphi1(double t)
{
// printf("dphi1: %g\n", halfpi * cosh(t) / sqr(cosh(halfpi * sinh(t))));
return halfpi * cosh(t) / sqr(cosh(halfpi * sinh(t)));
}
/* DE 公式による (a,b) における定積分の計算, x=a,b で桁落ち対策 */
double de3(ddfunction f, ddfunction g, ddfunction H,
double a, double b, double h, double N)
{
int n;
double p, q;
double t, S, dS;
p = (b - a) / 2.0; q = (a + b) / 2.0;
S = 0.0;
for (n = -N; n <= N; n++) {
t = n * h;
if (t > 0.5)
dS = dphi1(t) * g(p * xi(t));
else if (t < - 0.5)
dS = dphi1(t) * H(p * eta(t));
else
dS = dphi1(t) * f(p * phi1(t) + q);
S += dS;
}
return p * h * S;
}
/* テスト用の被積分関数 その1 */
double f1(double x)
{
return sqrt(1.0 - sqr(x));
}
// g1(y)=f1(y+b), b=1
// return f1(y + 1.0);
double g1(double y)
{
return sqrt(- y * (y + 2.0));
}
// h1(y)=f1(y+a), a=-1
double h1(double y)
// return f1(y - 1.0);
{
return sqrt(y * (2.0 - y));
}
/* テスト用の被積分関数 その2 */
double f2(double x)
{
return 1.0 / sqrt(1.0 - sqr(x));
}
double g2(double y)
// return f2(y + 1.0);
{
return 1.0 / sqrt(- y * (y + 2.0));
}
double h2(double y)
// return f2(y - 1.0);
{
return 1.0 / sqrt(y * (2.0 - y));
}
void test(ddfunction f, ddfunction g, ddfunction H, double I)
{
int m, N;
double h, IhN;
/* |t|≦4 まで計算することにする */
h = 1.0; N = 4;
/* h を半分, N を倍にして double exponential formula で計算してゆく */
for (m = 1; m <= 10; m++) {
IhN = de3(f, g, H, - 1.0, 1.0, h, N);
printf("h=%f, N=%4d, I_hN=%25.16f, I_hN-I=%e\n", h, N, IhN, IhN - I);
h /= 2; N *= 2;
}
}
int main(void)
{
pi = 4.0 * atan(1.0); halfpi = pi / 2;
printf("DE公式による数値積分\n");
printf("test1 (sqrt(1-x^2) の (-1,1) での積分)\n");
test(f1, g1, h1, pi / 2);
printf("test2 (1/sqrt(1-x^2) の (-1,1) での積分)\n");
test(f2, g2, h2, pi);
return 0;
}