<?php /*********************************************/ /* 多項式近似によるy=x^4+3x^3+2x^2+1の最小値 */ /* coded by Y.Suganuma */ /*********************************************/ $x = -3.0; $d = 0.1; $eps = 1.0e-7; $max = 100; $x = approx($x, $d, $eps, $val, $ind, $max, "snx"); printf("x %f val %f ind %d\n", $x, $val, $ind); /****************/ /* 関数値の計算 */ /****************/ function snx($x) { return $x * $x * $x * $x + 3.0 * $x * $x * $x + 2.0 * $x * $x + 1.0; } /******************************************/ /* 多項式近似(関数の最小値) */ /* x0 : 初期値 */ /* d0 : 初期ステップ */ /* eps : 許容誤差 */ /* val : 関数値 */ /* ind : 計算状況 */ /* >= 0 : 正常終了(収束回数) */ /* = -1 : 収束せず */ /* max : 最大試行回数 */ /* fun : 関数値を計算する関数の名前 */ /* return : 結果 */ /******************************************/ function approx($x0, $d0, $eps, &$val, &$ind, $max, $fun) { $xx = 0.0; $k = 0; $count = 0; $d = $d0; $x = array(4); $x[1] = $x0; $f = array(4); $f[1] = $fun($x0); $ind = -1; while ($count < $max && $ind < 0) { $x[3] = $x[1] + $d; $f[3] = $fun($x[3]); while ($k < $max && $f[3] <= $f[1]) { $k++; $d *= 2.0; $x[0] = $x[1]; $f[0] = $f[1]; $x[1] = $x[3]; $f[1] = $f[3]; $x[3] = $x[1] + $d; $f[3] = $fun($x[3]); } // 初期値が不適当 if ($k >= $max) $count = $max; else { // 3点の選択 $sw = 0; if ($k > 0) { $x[2] = $x[3] - 0.5 * $d; $f[2] = $fun($x[2]); $min = -1; for ($i1 = 0; $i1 < 4; $i1++) { if ($min < 0 || $f[$i1] < $f[$min]) $min = $i1; } if ($min >= 2) { for ($i1 = 0; $i1 < 3; $i1++) { $x[$i1] = $x[$i1+1]; $f[$i1] = $f[$i1+1]; } } $sw = 1; } else { $x[0] = $x[1] - $d0; $f[0] = $fun($x[0]); if ($f[0] > $f[1]) { $x[2] = $x[3]; $f[2] = $f[3]; $sw = 1; } else { $x[1] = $x[0]; $f[1] = $f[0]; $d0 = -$d0; $d = 2.0 * $d0; $k = 1; } } // 収束? if ($sw > 0) { $count++; $dl = 0.5 * $d * ($f[2] - $f[0]) / ($f[0] - 2.0 * $f[1] + $f[2]); $xx = $x[1] - $dl; $val = $fun($xx); if (abs($dl) < $eps) $ind = $count; else { $k = 0; $d0 = 0.5 * $d; $d = $d0; if ($val < $f[1]) { $x[1] = $xx; $f[1] = $val; } } } } } return $xx; } ?>