2.10.5.0.1 nint6.c


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

桂田 祐史
2016-03-13