<?php /***************************************/ /* 多次元ニュートン法 */ /* (3点(0.5,1.0),(0.0,1.5),(0.5,2.0) */ /* を通る円の中心と半径) */ /* coded by Y.Suganuma */ /***************************************/ $eps1 = 1.0e-7; $eps2 = 1.0e-10; $max = 20; $x = array(); $x0 = array(); $x0[0] = 0.0; $x0[1] = 0.0; $x0[2] = 2.0; $w = array(); for ($i1 = 0; $i1 < 3; $i1++) $w[$i1] = array(); $ind = newton("snx", "dsnx", $x0, $eps1, $eps2, $max, $w, 3, $x); printf("ind = %d\n", $ind); printf("x"); for ($i1 = 0; $i1 < 3; $i1++) printf(" %f", $x[$i1]); printf("\nf "); snx($x, $x0); for ($i1 = 0; $i1 < 3; $i1++) printf(" %f", $x0[$i1]); printf("\n"); /*****************************************************/ /* Newton法による非線形方程式(f(x)=0)の解 */ /* f : f(x)を計算する関数名 */ /* df : f(x)の微分を計算する関数名 */ /* x0 : 初期値 */ /* eps1 : 終了条件1(|x(k+1)-x(k)|<eps1) */ /* eps2 : 終了条件2(|f(x(k))|<eps2) */ /* max : 最大試行回数 */ /* w : f(x)の微分の逆行列を入れる (n x 2n) */ /* n : xの次数 */ /* x : 解 */ /* return : 実際の試行回数 */ /* (負の時は解を得ることができなかった) */ /*****************************************************/ function newton($f, $df, $x0, $eps1, $eps2, $max, $w, $n, &$x) { $ind = 0; $g = array($n); $x1 = array($n); $sw = 0; for ($i1 = 0; $i1 < $n; $i1++) { $x1[$i1] = $x0[$i1]; $x[$i1] = $x1[$i1]; } while ($sw == 0 && $ind >= 0) { $sw = 1; $ind++; $f($x1, $g); $sw1 = 0; for ($i1 = 0; $i1 < $n && $sw1 == 0; $i1++) { if (abs($g[$i1]) > $eps2) $sw1 = 1; } if ($sw1 > 0) { if ($ind <= $max) { $sw1 = $df($x1, $w, $eps2); if ($sw1 == 0) { for ($i1 = 0; $i1 < $n; $i1++) { $x[$i1] = $x1[$i1]; for ($i2 = 0; $i2 < $n; $i2++) $x[$i1] -= $w[$i1][$i2+$n] * $g[$i2]; } $sw1 = 0; for ($i1 = 0; $i1 < $n && $sw1 == 0; $i1++) { if (abs($x[$i1]-$x1[$i1]) > $eps1 && abs($x[$i1]-$x1[$i1]) > $eps1*abs($x[$i1])) $sw1 = 1; } if ($sw1 > 0) { for ($i1 = 0; $i1 < $n; $i1++) $x1[$i1] = $x[$i1]; $sw = 0; } } else $ind = -1; } else $ind = -1; } } return $ind; } /************************/ /* 関数値(f(x))の計算 */ /************************/ function snx($x, &$y) { $y[0] = (0.5 - $x[0]) * (0.5 - $x[0]) + (1.0 - $x[1]) * (1.0 - $x[1]) - $x[2] * $x[2]; $y[1] = (0.0 - $x[0]) * (0.0 - $x[0]) + (1.5 - $x[1]) * (1.5 - $x[1]) - $x[2] * $x[2]; $y[2] = (0.5 - $x[0]) * (0.5 - $x[0]) + (2.0 - $x[1]) * (2.0 - $x[1]) - $x[2] * $x[2]; } /*****************************************/ /* 関数の微分の計算 */ /* x : 変数値 */ /* w : 微分の逆行列(後半) */ /* eps : 逆行列の存在を判定する規準 */ /* return : =0 : 正常 */ /* =1 : 逆行列が存在しない */ /*****************************************/ function dsnx($x, &$w, $eps) { for ($i1 = 0; $i1 < 3; $i1++) { for ($i2 = 0; $i2 < 3; $i2++) $w[$i1][$i2+3] = 0.0; $w[$i1][$i1+3] = 1.0; } $w[0][0] = -2.0 * (0.5 - $x[0]); $w[0][1] = -2.0 * (1.0 - $x[1]); $w[0][2] = -2.0 * $x[2]; $w[1][0] = -2.0 * (0.0 - $x[0]); $w[1][1] = -2.0 * (1.5 - $x[1]); $w[1][2] = -2.0 * $x[2]; $w[2][0] = -2.0 * (0.5 - $x[0]); $w[2][1] = -2.0 * (2.0 - $x[1]); $w[2][2] = -2.0 * $x[2]; $sw = gauss($w, 3, 3, $eps); return $sw; } /******************************************/ /* 線形連立方程式を解く(逆行列を求める) */ /* $w : 方程式の左辺及び右辺 */ /* $n : 方程式の数 */ /* $m : 方程式の右辺の列の数 */ /* $eps : 逆行列の存在を判定する規準 */ /* return : =0 : 正常 */ /* =1 : 逆行列が存在しない */ /*******************************************/ function gauss(&$w, $n, $m, $eps) { $ind = 0; $nm = $n + $m; for ($i1 = 0; $i1 < $n && $ind == 0; $i1++) { $y1 = 0.0; $m1 = $i1 + 1; $m2 = 0; // ピボット要素の選択 for ($i2 = $i1; $i2 < $n; $i2++) { $y2 = abs($w[$i2][$i1]); if ($y1 < $y2) { $y1 = $y2; $m2 = $i2; } } // 逆行列が存在しない if ($y1 < $eps) $ind = 1; // 逆行列が存在する else { // 行の入れ替え for ($i2 = $i1; $i2 < $nm; $i2++) { $y1 = $w[$i1][$i2]; $w[$i1][$i2] = $w[$m2][$i2]; $w[$m2][$i2] = $y1; } // 掃き出し操作 $y1 = 1.0 / $w[$i1][$i1]; for ($i2 = $m1; $i2 < $nm; $i2++) $w[$i1][$i2] *= $y1; for ($i2 = 0; $i2 < $n; $i2++) { if ($i2 != $i1) { for ($i3 = $m1; $i3 < $nm; $i3++) $w[$i2][$i3] -= $w[$i2][$i1] * $w[$i1][$i3]; } } } } return $ind; } ?>