補間ライブラリ



[ 簡単な説明 ]

補間法(内挿法)関連のライブラリです。

最小2乗近似法は、標本点データから近似多項式の係数を求める手法です。

ラグランジェ補間、およびスプライン補間は、補間点xにおける補間値を求めます。
これらに入力する標本点配列 xdata [ ] とそれぞれの標本点における関数値配列 ydata [ ] は、x の昇順にあらかじめ整列しておく必要があります。また、補間点xは標本点の区間内にある必要があります。

チェビシェフ近似は、関数を多項式で近似する手法です。指定区間内で極めて高精度の多項式近似を行います。
チェビシェフ近似は、チェビシェフ多項式を用いて関数近似を行いますが、チェビシェフ多項式は、-1 ≦ x ≦ 1 の範囲で定義されるので、まず区間変換を行います。(1次の変数変換)
近似多項式の係数が求まったのち、区間の逆変換を行い、与えられた関数に対する近似多項式の係数を計算します。本プログラム中では、その逆変換を行うために、パスカルの三角形による2項係数の計算を行っています。

関数名 関数の機能 機能説明 呼び出し例
lstsq( ) 最小2乗近似法 n個の要素を持つ double型配列 x[ ]、y[ ]より
次数 m の多項式係数 c[ ] を求める
lstsq(x, y, n, m, c);
lagra( ) ラグランジェ補間 n個の点 xdata[ ],ydata[ ] から
点 x における補間値を求める
y = lagra(xdata, ydata, n, x);
splint( ) スプライン補間 y = splint(xdata, ydata, n, x);
chebyshev( ) チェビシェフ近似 多項式近似したい関数を double _f(x) で定義して、
近似範囲 min,max 、次数 n を与えると
チェビシェフ近似による係数配列 a[ ] を計算する
chebyshev(min, max, n, a);
polynomial( ) 多項式計算 係数配列 a[ ] 、値 x 、次数 n より
多項式の値を計算する
y = polynomial(a, x, n);

プログラム・ソース("interp.c")           top (トップに戻る)
/*		interp.c		*/
#include <stdio.h>
#include <stdlib.h>
#include "sslib.h"

void lstsq(double x[], double y[], int n, int m, double c[])
{
	int i, j, k, m2, mp1, mp2;
	double *a, aik, pivot, *w, w1, w2, w3;

	if(m >= n || m < 1)
	{
		fprintf(stderr, "Error : Illegal parameter in lstsq()\n");
		return;
	}
	mp1 = m + 1;
	mp2 = m + 2;
	m2 = 2 * m;
	a = (double *)malloc(mp1 * mp2 * sizeof(double));
	if(a == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in lstsq()\n");
		return;
	}
	w = (double *)malloc(mp1 * 2 * sizeof(double));
	if(w == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in lstsq()\n");
		free(a);
		return;
	}
	for(i = 0; i < m2; i++)
	{
		w1 = 0.;
		for(j = 0; j < n; j++)
		{
			w2 = w3 = x[j];
			for(k = 0; k < i; k++)	w2 *= w3;
			w1 += w2;
		}
		w[i] = w1;
	}
	a[0] = n;
	for(i = 0; i < mp1; i++)
		for(j = 0; j < mp1; j++)	if(i || j)	a[i * mp2 + j] = w[i + j - 1];

	w1 = 0.;
	for(i = 0; i < n; i++)	w1 += y[i];
	a[mp1] = w1;
	for(i = 0; i < m; i++)
	{
		w1 = 0.;
		for(j = 0; j < n; j++)
		{
			w2 = w3 = x[j];
			for(k = 0; k < i; k++)	w2 *= w3;
			w1 += y[j] * w2;
		}
		a[mp2 * (i + 1) + mp1] = w1;
	}

	for(k = 0; k < mp1; k++)
	{
		pivot = a[mp2 * k + k];
		a[mp2 * k + k] = 1.0;
		for(j = k + 1; j < mp2; j++)	a[mp2 * k + j] /= pivot;
		for(i = 0; i < mp1; i++)
		{
			if(i != k)
			{
				aik = a[mp2 * i + k];
				for(j = k; j < mp2; j++)
					a[mp2 * i + j] -= aik * a[mp2 * k + j];
			}
		}
	}
	for(i = 0; i < mp1; i++)	c[i] = a[mp2 * i + mp1];
	free(w);
	free(a);
	return;
}

double lagra(double xx[], double yy[], int n, double xi)
{
	int flag, i, j, *jun, *k;
	double *p, *q, *r, *s, w, w1, w2, w3, *x, *y;

	x = (double *)malloc(n * sizeof(double));
	if(x == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in lagra()\n");
		return 0.;
	}
	y = (double *)malloc(n * sizeof(double));
	if(y == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in lagra()\n");
		free((char *)x);
		return 0.;
	}
	for(i = 0, p = xx, flag = 0; i < n - 1; i++, p++)
	{
		if(*p >= *(p + 1))
		{
			flag = 1;
			break;
		}
	}
	if(flag)
	{
		jun = (int *)malloc(n * sizeof(int));
		if(jun == NULL)
		{
			fprintf(stderr, "Error : Out of memory  in lagra()\n");
			free((char *)x);
			free((char *)y);
			return 0.;
		}
		sortdi1(xx, n, jun);
		for(i = 0, p = x, q = y, k = jun; i < n; i++)
		{
			*p++ = *(xx + *k);
			*q++ = *(yy + *k++);
		}
		free((char *)jun);
	}
	else
	{
		for(i = 0, p = x, q = y, r = xx, s = yy; i < n; i++)
		{
			*p++ = *r++;
			*q++ = *s++;
		}
	}

	if(n < 2 || xi < *x || xi > *(x + n - 1))
	{
		fprintf(stderr, "Error : Illegal parameter  in lagra()\n");
		free((char *)x);
		free((char *)y);
		return 0.;
	}
	for(i = 0, p = x; i < n; i++)
	{
		if(*p++ == xi)
		{
			free((char *)x);
			free((char *)y);
			return *(y + i);
		}
	}
	w = 0.;
	for(i = 0, p = x, q = y; i < n; i++)
	{
		w1 = 1.;
		w2 = *p++;
		for(j = 0, r = x; j < n; j++)
		{
			w3 = *r++;
			if(i != j)	w1 *= (xi - w3) / (w2 - w3);
		}
		w += w1 * *q++;
	}
	free((char *)x);
	free((char *)y);
	return w;
}

double splint(double xx[], double yy[], int n, double xi)
{
	int flag, i, *jun, *k;
	double dxp, dxm, *h, hi, hi2, *p, *q, *r, *s, sm, si, *sp, *x, *y, yi;

	x = (double *)malloc(n * sizeof(double));
	if(x == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in splint()\n");
		return 0.;
	}
	y = (double *)malloc(n * sizeof(double));
	if(y == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in splint()\n");
		free((char *)x);
		return 0.;
	}
	for(i = 0, p = xx, flag = 0; i < n - 1; i++, p++)
	{
		if(*p >= *(p + 1))
		{
			flag = 1;
			break;
		}
	}
	if(flag)
	{
		jun = (int *)malloc(n * sizeof(int));
		if(jun == NULL)
		{
			fprintf(stderr, "Error : Out of memory  in splint()\n");
			free((char *)x);
			free((char *)y);
			return 0.;
		}
		sortdi1(xx, n, jun);
		for(i = 0, p = x, q = y, k = jun; i < n; i++)
		{
			*p++ = *(xx + *k);
			*q++ = *(yy + *k++);
		}
		free((char *)jun);
	}
	else
	{
		for(i = 0, p = x, q = y, r = xx, s = yy; i < n; i++)
		{
			*p++ = *r++;
			*q++ = *s++;
		}
	}

	if(n < 2 || xi < *x || xi > *(x + n - 1))
	{
		fprintf(stderr, "Error : Illegal parameter  in splint()\n");
		free((char *)x);
		free((char *)y);
		return 0.;
	}
	for(i = 0, p = x; i < n; i++)
	{
		if(*p++ == xi)
		{
			free((char *)x);
			free((char *)y);
			return *(y + i);
		}
	}
	h = (double *)malloc(n * sizeof(double));
	if(h == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in splint()\n");
		free((char *)x);
		free((char *)y);
		return 0.;
	}
	sp = (double *)malloc(n * sizeof(double));
	if(sp == NULL)
	{
		fprintf(stderr, "Error : Out of memory  in splint()\n");
		free((char *)x);
		free((char *)y);
		free((char *)h);
		return 0.;
	}

	subspl(x, y, n - 1, h, sp);
	for(i = 1, p = x + 1; i <= n; i++, p++)
	{
		if(*(p - 1) <= xi && xi < *p)
		{
			sm = *(sp + i - 1);
			si = *(sp + i);
			hi = *(h + i);
			hi2 = hi * hi;
			dxp = *p - xi;
			dxm = xi - *(p - 1);
			yi = (  sm * dxp * dxp * dxp + si * dxm * dxm * dxm
				  + (6. * *(y + i - 1) - hi2 * sm) * dxp
				  + (6. * *(y + i)     - hi2 * si) * dxm) / hi / 6.;
			free((char *)x);
			free((char *)y);
			free((char *)h);
			free((char *)sp);
			return yi;
		}
	}
	return 0;
}

double polynomial(double a[], double x, int n)
{
	int i;
	double w;

	w = a[n];
	for(i = n - 1; i >= 0; i--)	w = w * x + a[i];
	return w;
}

void chebyshev(double min, double max, int n, double a[])
{						/* min, max : range of approximation */
	double **T;
	double *c, *func, *x;
	double k1, k2, n1, w;
	int i, j;

	if(min == max || n <= 0)
	{
		fprintf(stderr, "Error : illegal parameter input in chebyshev()\n");
		return;
	}

	T = (double **)malloc((n + 1) * sizeof(double *));
	if(T == NULL)
	{
		fprintf(stderr, "Error : out of memory in chebyshev().\n");
		return;
	}
	for(i = 0; i <= n; i++)
	{
		T[i] = (double *)calloc(i + 1, sizeof(double));
		if(T[i] == NULL)
		{
			fprintf(stderr, "Error : out of memory in chebyshev().\n");
			return;
		}
	}

	c = (double *)calloc(n + 1, sizeof(double));
	func = (double *)calloc(n + 1, sizeof(double));
	x = (double *)calloc(n + 1, sizeof(double));
	if(c == NULL || func == NULL || x == NULL)
	{
		fprintf(stderr, "Error : out of memory in chebyshev().\n");
		return;
	}

	/*	Chebyshev Polynomial	*/
	T[0][0] = 1;
	T[1][0] = 0;
	T[1][1] = 1;
	for(i = 2; i <= n; i++)
	{
		for(j = 1; j <= i; j++)	T[i][j] = 2. * T[i - 1][j - 1];
		for(j = 0; j < i - 1; j++)	T[i][j] -= T[i - 2][j];
	}

	k1 = (max - min) / 2.;
	k2 = (min + max) / 2.;
	n1 = n + 1;

	w = 0.5 * M_PI / n1;
	for(i = 0; i <= n; i++)
	{
		x[i] = cos((2 * i + 1) * w);
		func[i] = _f(k1 * x[i] + k2);
	}

	w = 0.;
	for(j = 0; j <= n; j++)	w += func[j];
	c[0] = w / n1;

	for(i = 1; i <= n; i++)
	{
		w = 0.;
		for(j = 0; j <= n; j++)	w += func[j] * polynomial(T[i], x[j], i);
		c[i] = 2. * w / n1;
	}

	for(i = 0; i <= n; i++)	a[i] = 0;
	for(i = 0; i <= n; i++)
	{
		w = c[i];
		for(j = 0; j <= i; j++)	a[j] += w * T[i][j];
	}

	if(min != -1. || max != 1.)
	{
		for(i = 1; i <= n; i++)	for(j = 1; j <= i; j++)	a[i] /= k1;

		if(k2 != 0.)

		T[0][0] = 1;				/* Pascal's Triangle */
		for(i = 1; i <= n; i++)
		{
			T[i][0] = 1;
			T[i][i] = 1;
			for(j = 1; j < i; j++)	T[i][j] = T[i - 1][j] + T[i - 1][j - 1];
		}

		for(i = 0; i <= n; i++)
		{
			c[i] = a[i];
			w = -k2 * a[i];
			for(j = i - 1; j >= 0; j--)
			{
				c[j] += T[i][j] * w;
				w *= -k2;
			}
		}
		for(i = 0; i <= n; i++)	a[i] = c[i];
	}
	free((char *)c);
	free((char *)func);
	free((char *)x);
	for(i = 0; i <= n; i++)	free((char *)T[i]);
	free((char *)T);
	return;
}