[PR] この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。

数値微分と数値積分

関数f(x)のあるx=aでのf'(a),f"(a)を数値的に近似計算をする。

微分の定義は
 f'(a) = lim[h→0] (f(a+h) - f(a))/h
これを近似するのに
 lim[h→0] h = ⊿x
を代用すればよい、と考えるのが数値微分の基本的な発想である。

次のように前方差分、後方微分、中心微分を定義する。
前方後方中心差分の関係
前方差分(forward difference)
 ⊿y/⊿x = (f(x+⊿x) - f(x))/⊿x
後方差分または後退差分(backward difference)
 ∇y/∇x = (f(x) - f(x-⊿x))/⊿x
中心差分(central difference)
 δy/δx = (f(x+⊿x) - f(x-⊿x))/2⊿x
これは、⊿y/⊿xと∇y/∇xの平均となる。

テイラー展開をしてみる
 f(x+⊿x) = f(x)+f'(x)⊿x/1!+f"(x)(⊿x)2/2!+ … +f(n)(⊿x)n/n!+ …
これから
 {f(x+⊿x)-f(x)}/⊿x = f'(x)/1!+f"(x)(⊿x)1/2!+ … +f(n)(⊿x)n-1/n!+ …
 ∴⊿x/⊿y = f'(x)+f"(x)(⊿x)1/2!+ … +f(n)(⊿x)n-1/n!+ …

同様に
 f(x-⊿x) = f(x)-f'(x)⊿x/1!+f"(x)(⊿x)2/2!+ … -f(n)(⊿x)n/n!+ …
これから
 {f(x)-f(x-⊿x)}/⊿x = f'(x)/1!-f"(x)(⊿x)1/2!+ … +f(n)(⊿x)n-1/n!+ …
 ∴∇x/∇y = f'(x)-f"(x)(⊿x)1/2!+ … +f(n)(⊿x)n-1/n!+ …


n階微係数の近似
上のテイラー展開した式を用いる。
読み飛ばした者は結果(太文字)だけでも見るように。
 ⊿y/⊿x-∇y/∇x = f"(x)⊿x+f""(x)(⊿x)2/2・4!+ …
これを⊿xで割れば
 (⊿y/⊿x-∇y/∇x)/⊿x = f"(x)+f""(x)⊿x+ …
f""(x)⊿x以上の項は小さいので無視すれば
(⊿y/⊿x-∇y/∇x)/⊿xはf"(x)の近似とできる。

また、これを
 δ2y/δx2と表し、2階の中心差分という。

#include <stdio.h>
#include <math.h>
#define dx (0.1)	//⊿x
/**
 * xの関数f(x)について
 * f"(a)の近似値を求める。
 * ----
 * ここでは
 * f(x)=x^3
 * f"(x)=6xのハズだが…
 */
float f(float x){
	return pow(x,3);	//f(x)=x^3
}
main(){
	float x,fpp;
	for(x=0;x<10;x++){
		fpp=(f(x+dx)-2*f(x)+f(x-dx))/dx/dx;
		printf("f\"(%1.0f) = %f\n",x,fpp);
	}
}
f(x)=x3という式について
f"(x)を求めている。
式は単純なのでプログラムに任せるまでもなく、
f"(x)=6xとわかる。
実際にプログラムで、
f"(0)、f"(1)、f"(2) … f"(9)
の計算結果を表示している。
0,6,12,18,,,54
と、出力されれば完全だが…

誤差は⊿x(プログラムではdx)の二乗に比例する。
即ち、小さければ小さい程(理論上)精密になるが、それは桁数を
いくらでも取れる場合だけで、f(x+⊿x)-f(x)の結果が0や無意味な
数値になってしまう。

一般の場合
δny/δxnについては(n∈N)
nが偶数の時
 δny/δxn = {(δn-2y/δ(x+⊿x)n-2) - 2(δn-2y/δxn-2) + (δn-2y/δ(x-⊿x)n-2)}/⊿x2
nが奇数の時
 δny/δxn = {(δn-1y/δ(x+⊿x)n-1) - (δn-1y/δ(x-⊿x)n-1)}/2⊿x
これをスターリング(Stirling)の方式という。


数値積分
定積分は次のように表せる
 ∫[a~b] f(x)・dx = lim[n→∞](b-a)/nΣ[k=1~n]f(a+k(b-a)/n)
と書けてlim[n→∞]nの代わりに「十分大きなn」を使うことで近似する。

定積分は下の図の青色の斜線の面積を求めることになる。
台形公式の図
xのa~bをn等分し、端からx0(=a)、x1、x2、…xn-1、xn(=b)とする。
xkはa+k(b-a)/nである。
また、分割したxの幅を⊿xとする。
 ⊿x = (b-a)/n = xn-xn-1
よって、
 I = Σ⊿x*{f(xn)+f(xn+1)}/2

●f(x)=4/(1+x2)のxの定義域[0~1]でdxで積分する。

x=tanθとする。
 1/(1+x2)=cos2θ
 dx=1/cos2θ
であるから、
∫f(x)・dx = ∫1・dθ
     = π

#include <stdio.h>
/**
 * f(x) = 4/(1+x^2)の積分
 */
float f(float x){
	return 4/(1+x*x);
}
main(){
	float k,n,a,b;
	float sum=0.0,dx;
	
	printf("分割数 n=");scanf("%f",&n);
	a=0;b=1;
	dx=(b-a)/n;
	
	for(k=0;k<n;k++){
		sum += dx * (f(a+(b-a)*k/n)+f(a+(b-a)*(k+1)/n))/2;
	}
	printf("∫4/(1+x^2)・dx = %f",sum);
}
初めにnを指定する。
256とすると3.141590と表示され、大体πとなる。

ちなみに、k,n,a,bはint型でもいいような気がするが
dx=(b-a)/n
や(a+(b-a)*k/n)
が、勝手にintに縛られ、いちいち変数の前に(float)をつけるのは
面倒なので、初めにfloat型で宣言した。


参考:岩波出版 「情報処理入門コース7 数値計算」戸川隼人著
goto index