example6.c


/*
 * example6.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公式) ならばうまく計算できる。
 *
 *  コンパイル: cc -o example6 example6.c
 *
 */

#include <stdio.h>
#include <math.h>
#include <string.h>

typedef double ddfunction(double);

double pi, halfpi;

/* φ */
double phi1(double t)
{
  return tanh(halfpi * sinh(t));
}

/* 2乗 */
double sqr(double x) { return x * x; }

/* φ' */
double dphi1(double t)
{
  return halfpi * cosh(t) / sqr(cosh(halfpi * sinh(t)));
}

/* DE 公式による (-1,1) における定積分の計算 */
double de(ddfunction f, double h, double N)
{
  int n;
  double t, S, dS;
  S = f(phi1(0.0)) * dphi1(0.0);
  for (n = 1; n <= N; n++) {
    t = n * h;
    dS = f(phi1(t)) * dphi1(t) + f(phi1(-t)) * dphi1(- t);
    S += dS;
  }
  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) // φ(t)の計算で情報落ちで1になる場合の安全網
    return 0;
  else
      return 1 / sqrt(1 - x * x);
}

void test(ddfunction f, double I)
{
  int m, N;
  double h, IhN;

  /* |t|≦3 まで計算することにする */
  h = 1.0; N = 3;
  /* h を半分, N を倍にして double exponential formula で計算してゆく */
  for (m = 1; m <= 10; m++) {
    IhN = de(f, 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) の積分)\n");
  test(f1, halfpi);

  printf("test2 (1/sqrt(1-x^2) の積分)\n");
  test(f2, pi);

  return 0;
}

桂田 祐史
2018-08-13