データ例とプログラム
-------------------------i_data-------------------------
// 関数 a,一次元最適化を使用しない
関数 1 変数の数 2 最大試行回数 100 一次元最適化 0
許容誤差 1.0e-10 刻み幅 1.0
初期値 0.0 0.0
// 関数 a,一次元最適化を使用する
関数 1 変数の数 2 最大試行回数 100 一次元最適化 1
許容誤差 1.0e-10 刻み幅 1.0
初期値 0.0 0.0
// 関数 b,一次元最適化を使用しない
関数 2 変数の数 2 最大試行回数 100 一次元最適化 0
許容誤差 1.0e-10 刻み幅 1.0
初期値 0.0 0.0
// 関数 b,一次元最適化を使用する
関数 2 変数の数 2 最大試行回数 100 一次元最適化 1
許容誤差 1.0e-10 刻み幅 0.1
初期値 0.0 0.0
// 関数 c,一次元最適化を使用しない
関数 3 変数の数 2 最大試行回数 100 一次元最適化 0
許容誤差 1.0e-10 刻み幅 1.0
初期値 1.0 0.0
// 関数 c,一次元最適化を使用する
関数 3 変数の数 2 最大試行回数 100 一次元最適化 1
許容誤差 1.0e-10 刻み幅 1.0
初期値 1.0 0.0
-------------------------プログラム-------------------------
/******************************/
/* Newton法による最小値の計算 */
/* coded by Y.Suganuma */
/******************************/
import java.io.*;
import java.util.*;
public class Test {
public static void main(String args[]) throws IOException
{
double eps, H[][], step, x[], dx[], y[];
int fun, i1, max, n, opt_1, sw = 0;
StringTokenizer str;
String line;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// データの入力
// 1 行目
line = in.readLine();
str = new StringTokenizer(line, " ");
line = str.nextToken();
line = str.nextToken();
fun = Integer.parseInt(line);
line = str.nextToken();
line = str.nextToken();
n = Integer.parseInt(line);
line = str.nextToken();
line = str.nextToken();
max = Integer.parseInt(line);
line = str.nextToken();
line = str.nextToken();
opt_1 = Integer.parseInt(line);
// 2 行目
line = in.readLine();
str = new StringTokenizer(line, " ");
line = str.nextToken();
line = str.nextToken();
eps = Double.parseDouble(line);
line = str.nextToken();
line = str.nextToken();
step = Double.parseDouble(line);
// 3 行目
x = new double [n];
dx = new double [n];
y = new double [1];
H = new double [n][2*n];
line = in.readLine();
str = new StringTokenizer(line, " ");
line = str.nextToken();
for (i1 = 0; i1 < n; i1++) {
line = str.nextToken();
x[i1] = Double.parseDouble(line);
}
// 実行
Kansu kn = new Kansu(fun);
sw = kn.Newton(opt_1, max, n, eps, step, y, x, dx, H);
// 結果の出力
if (sw < 0) {
System.out.print(" 収束しませんでした!");
switch (sw) {
case -1:
System.out.println("(収束回数)");
break;
case -2:
System.out.print("(1次元最適化の区間)");
break;
case -3:
System.out.print("(黄金分割法)");
break;
}
}
else {
System.out.print(" 結果=");
for (i1 = 0; i1 < n; i1++)
System.out.print(x[i1] + " ");
System.out.println(" 最小値=" + y[0] + " 回数=" + sw);
}
}
}
/************************/
/* 関数値,微係数の計算 */
/************************/
class Kansu extends Newton
{
private int sw;
// コンストラクタ
Kansu (int s) {sw = s;}
// 与えられた点xから,dx方向へk*dxだけ進んだ
// 点における関数値を計算する
double snx(double k, double x[], double dx[])
{
double x1, y1, z1, y = 0.0, w[] = new double [2];
switch (sw) {
case 1:
w[0] = x[0] + k * dx[0];
w[1] = x[1] + k * dx[1];
x1 = w[0] - 1.0;
y1 = w[1] - 2.0;
y = x1 * x1 + y1 * y1;
break;
case 2:
w[0] = x[0] + k * dx[0];
w[1] = x[1] + k * dx[1];
x1 = w[1] - w[0] * w[0];
y1 = 1.0 - w[0];
y = 100.0 * x1 * x1 + y1 * y1;
break;
case 3:
w[0] = x[0] + k * dx[0];
w[1] = x[1] + k * dx[1];
x1 = 1.5 - w[0] * (1.0 - w[1]);
y1 = 2.25 - w[0] * (1.0 - w[1] * w[1]);
z1 = 2.625 - w[0] * (1.0 - w[1] * w[1] * w[1]);
y = x1 * x1 + y1 * y1 + z1 * z1;
break;
}
return y;
}
// 関数の微分
void dsnx(double x[], double dx[])
{
switch (sw) {
case 1:
dx[0] = -2.0 * (x[0] - 1.0);
dx[1] = -2.0 * (x[1] - 2.0);
break;
case 2:
dx[0] = 400.0 * x[0] * (x[1] - x[0] * x[0]) + 2.0 * (1.0 - x[0]);
dx[1] = -200.0 * (x[1] - x[0] * x[0]);
break;
case 3:
dx[0] = 2.0 * (1.0 - x[1]) * (1.5 - x[0] * (1.0 - x[1])) +
2.0 * (1.0 - x[1] * x[1]) * (2.25 - x[0] * (1.0 - x[1] * x[1])) +
2.0 * (1.0 - x[1] * x[1] * x[1]) * (2.625 - x[0] * (1.0 - x[1] * x[1] * x[1]));
dx[1] = -2.0 * x[0] * (1.5 - x[0] * (1.0 - x[1])) -
4.0 * x[0] * x[1] * (2.25 - x[0] * (1.0 - x[1] * x[1])) -
6.0 * x[0] * x[1] * x[1] * (2.625 - x[0] * (1.0 - x[1] * x[1] * x[1]));
break;
}
}
// Hesse行列の逆行列
int hesse(double x[], double H[][], double eps)
{
double x1, x2, x3;
int sw1 = 0;
switch (sw) {
case 1:
H[0][0] = 2.0;
H[0][1] = 0.0;
H[1][0] = 0.0;
H[1][1] = 2.0;
H[0][2] = 1.0;
H[0][3] = 0.0;
H[1][2] = 0.0;
H[1][3] = 1.0;
sw1 = gauss(H, 2, 2, eps);
break;
case 2:
H[0][0] = 400.0 * (3.0 * x[0] * x[0] - x[1]) + 2.0;
H[0][1] = -400.0 * x[0];
H[1][0] = H[0][1];
H[1][1] = 200.0;
H[0][2] = 1.0;
H[0][3] = 0.0;
H[1][2] = 0.0;
H[1][3] = 1.0;
sw1 = gauss(H, 2, 2, eps);
break;
case 3:
x1 = 1.0 - x[1];
x2 = 1.0 - x[1] * x[1];
x3 = 1.0 - x[1] * x[1] * x[1];
H[0][0] = 2.0 * x1 * x1 + 2.0 * x2 * x2 + 2.0 * x3 * x3;
H[0][1] = 2.0 * (1.5 - x[0] * x1) - 2.0 * x[0] * x1 +
4.0 * x[1] * (2.25 - x[0] * x2) - 4.0 * x[0] * x[1] * x2 +
6.0 * x[1] * x[1] * (2.625 - x[0] * x3) - 6.0 * x[0] * x[1] * x[1] * x3;
H[1][0] = H[0][1];
H[1][1] = 2.0 * x[0] * x[0] +
4.0 * x[0] * (2.25 - x[0] * x2) + 8.0 * x[0] * x[0] * x[1] * x[1] +
12.0 * x[0] * x[1] * (2.625 - x[0] * x3) +
18.0 * x[0] * x[0] * x[1] * x[1] * x[1] * x[1];
H[0][2] = 1.0;
H[0][3] = 0.0;
H[1][2] = 0.0;
H[1][3] = 1.0;
sw1 = gauss(H, 2, 2, eps);
break;
}
return sw1;
}
}
abstract class Newton {
/********************************************************/
/* Newton法 */
/* opt_1 : =0 : 1次元最適化を行わない */
/* =1 : 1次元最適化を行う */
/* max : 最大繰り返し回数 */
/* n : 次元 */
/* eps : 収束判定条件 */
/* step : きざみ幅 */
/* y : 最小値 */
/* x : x(初期値と答え) */
/* dx : 関数の微分値 */
/* H : Hesse行列の逆行列 */
/* return : >=0 : 正常終了(収束回数) */
/* =-1 : 収束せず */
/* =-2 : 1次元最適化の区間が求まらない */
/* =-3 : 黄金分割法が失敗 */
/********************************************************/
abstract double snx(double k, double x[], double dx[]);
abstract void dsnx(double x[], double dx[]);
abstract int hesse(double x[], double H[][], double eps);
int Newton(int opt_1, int max, int n, double eps, double step, double y[], double x[], double dx[], double H[][])
{
double f1, f2, k, sp, y1[] = new double [1], y2[] = new double [1];
double wk[] = new double [n];
int count = 0, i1, i2, sw = 0, sw1[] = new int [1];
y1[0] = snx(0.0, x, dx);
while (count < max && sw == 0) {
// 傾きの計算
dsnx(x, wk);
// Hesse行列の逆行列の計算
sw1[0] = hesse(x, H, eps);
// 収束していない
if (sw1[0] == 0) {
// 方向の計算
count++;
for (i1 = 0; i1 < n; i1++) {
dx[i1] = 0.0;
for (i2 = 0; i2 < n; i2++)
dx[i1] += H[i1][n+i2] * wk[i2];
}
// 1次元最適化を行わない
if (opt_1 == 0) {
// 新しい点
for (i1 = 0; i1 < n; i1++)
x[i1] += dx[i1];
// 新しい関数値
y2[0] = snx(0.0, x, dx);
// 関数値の変化が大きい
if (Math.abs(y2[0]-y1[0]) > eps)
y1[0] = y2[0];
// 収束(関数値の変化<eps)
else {
sw = count;
y[0] = y2[0];
}
}
// 1次元最適化を行う
else {
// 区間を決める
sw1[0] = 0;
f1 = y1[0];
sp = step;
f2 = snx(sp, x, dx);
if (f2 > f1)
sp = -step;
for (i1 = 0; i1 < max && sw1[0] == 0; i1++) {
f2 = snx(sp, x, dx);
if (f2 > f1)
sw1[0] = 1;
else {
sp *= 2.0;
f1 = f2;
}
}
// 区間が求まらない
if (sw1[0] == 0)
sw = -2;
// 区間が求まった
else {
// 黄金分割法
k = gold(0.0, sp, eps, y2, sw1, max, x, dx);
// 黄金分割法が失敗
if (sw1[0] < 0)
sw = -3;
// 黄金分割法が成功
else {
// 新しい点
for (i1 = 0; i1 < n; i1++)
x[i1] += k * dx[i1];
// 関数値の変化が大きい
if (Math.abs(y1[0]-y2[0]) > eps)
y1[0] = y2[0];
// 収束(関数値の変化<eps)
else {
sw = count;
y[0] = y2[0];
}
}
}
}
}
// 収束(傾き<eps)
else {
sw = count;
y[0] = y1[0];
}
}
if (sw == 0)
sw = -1;
return sw;
}
/****************************************************************/
/* 黄金分割法(与えられた方向での最小値) */
/* a,b : 初期区間 a < b */
/* eps : 許容誤差 */
/* val : 関数値 */
/* ind : 計算状況 */
/* >= 0 : 正常終了(収束回数) */
/* = -1 : 収束せず */
/* max : 最大試行回数 */
/* w : 位置 */
/* dw : 傾きの成分 */
/* return : 結果(w+y*dwのy) */
/****************************************************************/
double gold(double a, double b, double eps, double val[], int ind[], int max, double w[], double dw[])
{
double f1, f2, fa, fb, tau = (Math.sqrt(5.0) - 1.0) / 2.0, x = 0.0, x1, x2;
int count = 0;
// 初期設定
ind[0] = -1;
x1 = b - tau * (b - a);
x2 = a + tau * (b - a);
f1 = snx(x1, w, dw);
f2 = snx(x2, w, dw);
// 計算
while (count < max && ind[0] < 0) {
count += 1;
if (f2 > f1) {
if (Math.abs(b-a) < eps) {
ind[0] = 0;
x = x1;
val[0] = f1;
}
else {
b = x2;
x2 = x1;
x1 = a + (1.0 - tau) * (b - a);
f2 = f1;
f1 = snx(x1, w, dw);
}
}
else {
if (Math.abs(b-a) < eps) {
ind[0] = 0;
x = x2;
val[0] = f2;
f1 = f2;
}
else {
a = x1;
x1 = x2;
x2 = b - (1.0 - tau) * (b - a);
f1 = f2;
f2 = snx(x2, w, dw);
}
}
}
// 収束した場合の処理
if (ind[0] == 0) {
ind[0] = count;
fa = snx(a, w, dw);
fb = snx(b, w, dw);
if (fb < fa) {
a = b;
fa = fb;
}
if (fa < f1) {
x = a;
val[0] = fa;
}
}
return x;
}
/*******************************************************/
/* 線形連立方程式を解く(逆行列を求める) */
/* w : 方程式の左辺及び右辺 */
/* n : 方程式の数 */
/* m : 方程式の右辺の列の数 */
/* eps : 正則性を判定する規準 */
/* return : =0 : 正常 */
/* =1 : 逆行列が存在しない */
/*******************************************************/
int gauss(double w[][], int n, int m, double eps)
{
double y1, y2;
int ind = 0, nm, m1, m2, i1, i2, i3;
nm = n + m;
for (i1 = 0; i1 < n && ind == 0; i1++) {
y1 = .0;
m1 = i1 + 1;
m2 = 0;
for (i2 = i1; i2 < n; i2++) {
y2 = Math.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);
}
}