example6kai.c

(参考のため、かなり粗雑な書き方だが見せることにする。)


/*
 * 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;
}



桂田 祐史