<?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;
}
?>