情報学部 | 菅沼ホーム | 目次 | 索引 |
/******************************/ /* Newton法による最小値の計算 */ /* coded by Y.Suganuma */ /******************************/ #include <stdio.h> void dsnx1(double *, double *); double snx1(double, double *, double *); int hesse1(double *, double **, double); void dsnx2(double *, double *); double snx2(double, double *, double *); int hesse2(double *, double **, double); void dsnx3(double *, double *); double snx3(double, double *, double *); int hesse3(double *, double **, double); int Newton(int, int, int, double, double, double *, double *, double *, double **, double (*)(double, double *, double *), void (*)(double *, double *), int (*)(double *, double **, double)); int main() { double eps, **H, step, *x, *dx, y; int fun, i1, max, n, opt_1, sw = 0; // データの入力 scanf("%*s %d %*s %d %*s %d %*s %d", &fun, &n, &max, &opt_1); scanf("%*s %lf %*s %lf", &eps, &step); x = new double [n]; dx = new double [n]; H = new double * [n]; scanf("%*s"); for (i1 = 0; i1 < n; i1++) { scanf("%lf", &x[i1]); H[i1] = new double [2*n]; } // 実行 switch (fun) { case 1: sw = Newton(opt_1, max, n, eps, step, &y, x, dx, H, snx1, dsnx1, hesse1); break; case 2: sw = Newton(opt_1, max, n, eps, step, &y, x, dx, H, snx2, dsnx2, hesse2); break; case 3: sw = Newton(opt_1, max, n, eps, step, &y, x, dx, H, snx3, dsnx3, hesse3); break; } // 結果の出力 if (sw < 0) { printf(" 収束しませんでした!"); switch (sw) { case -1: printf("(収束回数)\n"); break; case -2: printf("(1次元最適化の区間)\n"); break; case -3: printf("(黄金分割法)\n"); break; } } else { printf(" 結果="); for (i1 = 0; i1 < n; i1++) printf("%f ", x[i1]); printf(" 最小値=%f 回数=%d\n", y, sw); } return 0; } /********************************************************/ /* Newton法 */ /* opt_1 : =0 : 1次元最適化を行わない */ /* =1 : 1次元最適化を行う */ /* max : 最大繰り返し回数 */ /* n : 次元 */ /* eps : 収束判定条件 */ /* step : きざみ幅 */ /* y : 最小値 */ /* x : x(初期値と答え) */ /* dx : 関数の微分値 */ /* H : Hesse行列の逆行列 */ /* snx : 関数値を計算する関数名 */ /* dsnx : 関数の微分を計算する関数名(符号を変える) */ /* hesse : Hesse行列の逆行列を計算する関数名 */ /* return : >=0 : 正常終了(収束回数) */ /* =-1 : 収束せず */ /* =-2 : 1次元最適化の区間が求まらない */ /* =-3 : 黄金分割法が失敗 */ /********************************************************/ #include <math.h> double gold(double, double, double, double *, int *, int, double *, double *, double (*)(double, double *, double *)); int Newton(int opt_1, int max, int n, double eps, double step, double *y, double *x, double *dx, double **H, double (*snx)(double, double *, double *), void (*dsnx)(double *, double *), int (*hesse)(double *, double **, double)) { double f1, f2, k, sp, y1, y2; double *wk = new double [n]; int count = 0, i1, i2, sw = 0, sw1; y1 = snx(0.0, x, dx); while (count < max && sw == 0) { // 傾きの計算 dsnx(x, wk); // Hesse行列の逆行列の計算 sw1 = hesse(x, H, eps); // 収束していない if (sw1 == 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 = snx(0.0, x, dx); // 関数値の変化が大きい if (fabs(y2-y1) > eps) y1 = y2; // 収束(関数値の変化<eps) else { sw = count; *y = y2; } } // 1次元最適化を行う else { // 区間を決める sw1 = 0; f1 = y1; sp = step; f2 = snx(sp, x, dx); if (f2 > f1) sp = -step; for (i1 = 0; i1 < max && sw1 == 0; i1++) { f2 = snx(sp, x, dx); if (f2 > f1) sw1 = 1; else { sp *= 2.0; f1 = f2; } } // 区間が求まらない if (sw1 == 0) sw = -2; // 区間が求まった else { // 黄金分割法 k = gold(0.0, sp, eps, &y2, &sw1, max, x, dx, fn); // 黄金分割法が失敗 if (sw1 < 0) sw = -3; // 黄金分割法が成功 else { // 新しい点 for (i1 = 0; i1 < n; i1++) x[i1] += k * dx[i1]; // 関数値の変化が大きい if (fabs(y1-y2) > eps) y1 = y2; // 収束(関数値の変化<eps) else { sw = count; *y = y2; } } } } } // 収束(傾き<eps) else { sw = count; *y = y1; } } if (sw == 0) sw = -1; delete [] wk; return sw; } /****************************************************************/ /* 黄金分割法(与えられた方向での最小値) */ /* a,b : 初期区間 a < b */ /* eps : 許容誤差 */ /* val : 関数値 */ /* ind : 計算状況 */ /* >= 0 : 正常終了(収束回数) */ /* = -1 : 収束せず */ /* max : 最大試行回数 */ /* w : 位置 */ /* dw : 傾きの成分 */ /* snx : 関数値を計算する関数の名前 */ /* return : 結果(w+y*dwのy) */ /****************************************************************/ #include <math.h> double gold(double a, double b, double eps, double *val, int *ind, int max, double *w, double *dw, double (*snx)(double, double *, double *)) { double f1, f2, fa, fb, tau = (sqrt(5.0) - 1.0) / 2.0, x = 0.0, x1, x2; int count = 0; // 初期設定 *ind = -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) { count += 1; if (f2 > f1) { if (fabs(b-a) < eps) { *ind = 0; x = x1; *val = f1; } else { b = x2; x2 = x1; x1 = a + (1.0 - tau) * (b - a); f2 = f1; f1 = snx(x1, w, dw); } } else { if (fabs(b-a) < eps) { *ind = 0; x = x2; *val = 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) { *ind = count; fa = snx(a, w, dw); fb = snx(b, w, dw); if (fb < fa) { a = b; fa = fb; } if (fa < f1) { x = a; *val = fa; } } return x; } /*********************************************/ /* 与えられた点xから,dx方向へk*dxだけ進んだ */ /* 点における関数値を計算する */ /*********************************************/ // 関数1 double snx1(double k, double *x, double *dx) { double x1, y1, y, w[2]; 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; return y; } // 関数2 double snx2(double k, double *x, double *dx) { double x1, y1, y, w[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; return y; } // 関数3 double snx3(double k, double *x, double *dx) { double x1, y1, z1, y, w[2]; 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; return y; } /********************/ /* 微係数を計算する */ /********************/ // 関数1 void dsnx1(double *x, double *dx) { dx[0] = -2.0 * (x[0] - 1.0); dx[1] = -2.0 * (x[1] - 2.0); } // 関数2 void dsnx2(double *x, double *dx) { 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]); } // 関数3 void dsnx3(double *x, double *dx) { 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])); } /*******************************/ /* Hesse行列の逆行列を計算する */ /*******************************/ int Gauss(double **, int, int, double); // 関数1 int hesse1(double *x, double **H, double eps) { int sw; 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; sw = Gauss(H, 2, 2, eps); return sw; } // 関数2 int hesse2(double *x, double **H, double eps) { int sw; 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; sw = Gauss(H, 2, 2, eps); return sw; } // 関数3 int hesse3(double *x, double **H, double eps) { double x1, x2, x3; int sw; 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; sw = Gauss(H, 2, 2, eps); return sw; } /*******************************************************/ /* 線形連立方程式を解く(逆行列を求める) */ /* w : 方程式の左辺及び右辺 */ /* n : 方程式の数 */ /* m : 方程式の右辺の列の数 */ /* eps : 正則性を判定する規準 */ /* return : =0 : 正常 */ /* =1 : 逆行列が存在しない */ /*******************************************************/ #include <math.h> 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 = fabs(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); }
/******************************/ /* 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); } }
<!DOCTYPE HTML> <HTML> <HEAD> <TITLE>最適化(Newton 法)</TITLE> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> <SCRIPT TYPE="text/javascript"> str1 = ""; str2 = new Array(); str3 = new Array(); function main() { // データの設定 let n = parseInt(document.getElementById("n").value); let max = parseInt(document.getElementById("max").value); let step = parseFloat(document.getElementById("step").value); let x = document.getElementById("x0").value.split(/ {1,}/) for (let i1 = 0; i1 < n; i1++) x[i1] = parseFloat(x[i1]); let opt_1 = 0; if (document.getElementById("line").options[1].selected) opt_1 = 1; str1 = document.getElementById("func").value + ";"; str2 = document.getElementById("dfunc").value.split(/\n{1,}/) for (let i1 = 0; i1 < n; i1++) str2[i1] = str2[i1] + ";"; str3 = document.getElementById("hesse").value.split(/\n{1,}/) for (let i1 = 0; i1 < n*n; i1++) str3[i1] = str3[i1] + ";"; let eps = 1.0e-10; let y = new Array(); let dx = new Array(); let H = new Array(); for (let i1 = 0; i1 < n; i1++) { dx[i1] = 0.0; H[i1] = new Array(); } // 実行 sw = newton(opt_1, max, n, eps, step, y, x, dx, H, snx, dsnx); // 結果 let res; if (sw < 0) { res = " 収束しませんでした!"; switch (sw) { case -1: res += "(収束回数)"; break; case -2: res += "(1次元最適化の区間)"; break; case -3: res += "(黄金分割法)"; break; } document.getElementById("res").value = res; } else { let c = 100000; res = " x ="; for (let i1 = 0; i1 < n; i1++) { let s1 = Math.round(x[i1] * c) / c; res = res + " " + s1; } res += "\n"; let s2 = Math.round(y[0] * c) / c; res = res + " 最小値 = " + s2 + " 回数 = " + sw; document.getElementById("res").value = res; } } /****************/ /* 関数値の計算 */ /****************/ function snx(k, x, dx) { let y = eval(str1); return y; } /************************/ /* 関数値の微分値の計算 */ /************************/ function dsnx(n, x, dx) { for (let i1 = 0; i1 < n; i1++) dx[i1] = eval(str2[i1]); } /******************************************************/ /* Newton法 */ /* opt_1 : =0 : 1次元最適化を行わない */ /* =1 : 1次元最適化を行う */ /* max : 最大繰り返し回数 */ /* n : 次元 */ /* eps : 収束判定条件 */ /* step : きざみ幅 */ /* y : 最小値 */ /* x : x(初期値と答え) */ /* dx : 関数の微分値 */ /* H : Hesse行列の逆行列 */ /* fn : 関数値を計算する関数 */ /* dfn : 関数の微分値を計算する関数 */ /* return : >=0 : 正常終了(収束回数) */ /* =-1 : 収束せず */ /* =-2 : 1次元最適化の区間が求まらない */ /* =-3 : 黄金分割法が失敗 */ /******************************************************/ function newton(opt_1, max, n, eps, step, y, x, dx, H, fn, dfn) { let f1, f2, k, sp, count = 0, sw = 0; let y1 = new Array(); let y2 = new Array(); let wk = new Array(); let sw1 = new Array(); y1[0] = fn(0.0, x, dx); while (count < max && sw == 0) { // 傾きの計算 dfn(n, x, wk); // Hesse行列の逆行列の計算 sw1[0] = hesse(n, x, H, eps); // 収束していない if (sw1[0] == 0) { // 方向の計算 count++; for (let i1 = 0; i1 < n; i1++) { dx[i1] = 0.0; for (let i2 = 0; i2 < n; i2++) dx[i1] += H[i1][n+i2] * wk[i2]; } // 1次元最適化を行わない if (opt_1 == 0) { // 新しい点 for (let i1 = 0; i1 < n; i1++) x[i1] += dx[i1]; // 新しい関数値 y2[0] = fn(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 = fn(sp, x, dx); if (f2 > f1) sp = -step; for (let i1 = 0; i1 < max && sw1[0] == 0; i1++) { f2 = fn(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, snx); // 黄金分割法が失敗 if (sw1[0] < 0) sw = -3; // 黄金分割法が成功 else { // 新しい点 for (let 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 : 傾きの成分 */ /* fn : 関数値を計算する関数 */ /* return : 結果(w+y*dwのy) */ /******************************************/ function gold(a, b, eps, val, ind, max, w, dw, fn) { let f1, f2, fa, fb, tau = (Math.sqrt(5.0) - 1.0) / 2.0, x = 0.0, x1, x2; let count = 0; // 初期設定 ind[0] = -1; x1 = b - tau * (b - a); x2 = a + tau * (b - a); f1 = fn(x1, w, dw); f2 = fn(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 = fn(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 = fn(x2, w, dw); } } } // 収束した場合の処理 if (ind[0] == 0) { ind[0] = count; fa = fn(a, w, dw); fb = fn(b, w, dw); if (fb < fa) { a = b; fa = fb; } if (fa < f1) { x = a; val[0] = fa; } } return x; } /*********************/ /* Hesse行列の逆行列 */ /*********************/ function hesse(n, x, H, eps) { let sw1 = 0; for (let i1 = 0; i1 < n; i1++) { for (let i2 = 0; i2 < n; i2++) H[i1][i2] = eval(str3[i1*n+i2]); } for (let i1 = 0; i1 < n; i1++) { for (let i2 = 0; i2 < n; i2++) { if (i1 == i2) H[i1][n+i2] = 1.0; else H[i1][n+i2] = 0.0; } } sw1 = Gauss(H, n, n, eps); return sw1; } /******************************************/ /* 線形連立方程式を解く(逆行列を求める) */ /* w : 方程式の左辺及び右辺 */ /* n : 方程式の数 */ /* m : 方程式の右辺の列の数 */ /* eps : 正則性を判定する規準 */ /* return : =0 : 正常 */ /* =1 : 逆行列が存在しない */ /******************************************/ function Gauss(w, n, m, eps) { let y1, y2, ind = 0, nm, m1, m2; nm = n + m; for (let i1 = 0; i1 < n && ind == 0; i1++) { y1 = .0; m1 = i1 + 1; m2 = 0; for (let 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 (let 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 (let i2 = m1; i2 < nm; i2++) w[i1][i2] *= y1; for (let i2 = 0; i2 < n; i2++) { if (i2 != i1) { for (let i3 = m1; i3 < nm; i3++) w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } return(ind); } </SCRIPT> </HEAD> <BODY STYLE="font-size: 130%; background-color: #eeffee;"> <H2 STYLE="text-align:center"><B>最適化(Newton 法)</B></H2> <DL> <DT> テキストフィールドおよびテキストエリアには,例として,以下に示すような関数の最小値を求める場合に対する値が設定されています(他の問題を実行する場合は,それらを適切に修正してください). <P STYLE="text-align:center"><IMG SRC="newton.gif"></P> <DT>n 変数関数 f(<B>x</B>) に対する Newton 法においては,現在の点 <B>x</B> における傾き, <P STYLE="text-align:center"><IMG SRC="newton1.gif"></P> <DT>と Hesse 行列の計算結果から方向 <B>p</B> を求め,その方向にαだけ進んだ点の関数値 f( <B>x</B> + α<B>p</B>) を計算する,という処理を繰り返します.目的関数の箇所には,この例に対する f( <B>x</B> + α<B>p</B>) の計算式,目的関数の微分の箇所には,<B>g</B> の計算式が与えられています.ただし,プログラム内では,変数 x は,この例の x と y からなる配列,変数 dx は <B>g</B> に対応し,関数 f を x 及び y で微分したものからなる配列,また,k はαに対応する変数として表現されています.また,Hesse 行列は,1 行 1 列,1 行 2 列,・・・,n 行 n 列の順で入力して下さい. なお,目的関数,目的関数の微分,及び,Hesse 行列は,JavaScript の仕様に適合した形式で記述してあることに注意してください. </DL> <DIV STYLE="text-align:center"> 変数の数(n):<INPUT ID="n" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="2"> 最大繰り返し回数:<INPUT ID="max" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="200"> 刻み幅:<INPUT ID="step" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="0.1"><BR> 初期値(n個):<INPUT ID="x0" STYLE="font-size: 100%" TYPE="text" SIZE="20" VALUE="0.0 0.0"> 1次元最適化:<SELECT STYLE="font-size: 100%" SIZE="1" ID="line"> <OPTION VALUE="no"> 行わない </OPTION><BR> <OPTION VALUE="yes"> 行う </OPTION><BR> </SELECT><BR><BR> 目的関数: f(x+αp) = <INPUT ID="func" STYLE="font-size: 100%" TYPE="text" SIZE="70" VALUE="100.0 * (x[1] + k * dx[1] - (x[0] + k * dx[0]) * (x[0] + k * dx[0])) * (x[1] + k * dx[1] - (x[0] + k * dx[0]) * (x[0] + k * dx[0])) + (1.0 - (x[0] + k * dx[0])) * (1.0 - (x[0] + k * dx[0]))"><BR><BR> 目的関数の微分: f'(x) = <TEXTAREA ID="dfunc" STYLE="font-size: 110%" COLS="70" ROWS="10"> 400.0 * x[0] * (x[1] - x[0] * x[0]) + 2.0 * (1.0 - x[0]) -200.0 * (x[1] - x[0] * x[0]) </TEXTAREA><BR><BR> Hesse行列: H = <TEXTAREA ID="hesse" STYLE="font-size: 110%" COLS="70" ROWS="10"> 400.0 * (3.0 * x[0] * x[0] - x[1]) + 2.0 -400.0 * x[0] 400.0 * (3.0 * x[0] * x[0] - x[1]) + 2.0 200.0 </TEXTAREA><BR><BR> <BUTTON STYLE="font-size: 100%; background-color: pink" onClick="main()">実行</BUTTON><BR><BR> 結果:<TEXTAREA ID="res" STYLE="font-size: 110%" COLS="40" ROWS="2"></TEXTAREA> </DIV> </BODY> </HTML>
<?php /******************************/ /* Newton法による最小値の計算 */ /* coded by Y.Suganuma */ /******************************/ $sw = 0; // データの入力 fscanf(STDIN, "%*s %d %*s %d %*s %d %*s %d", $fun, $n, $max, $opt_1); fscanf(STDIN, "%*s %lf %*s %lf", $eps, $step); $x = array($n); $dx = array(0.0, 0.0); $H = array($n); $str = trim(fgets(STDIN)); strtok($str, " "); for ($i1 = 0; $i1 < $n; $i1++) { $x[$i1] = floatval(strtok(" ")); $H[$i1] = array(2*$n); } // 実行 switch ($fun) { case 1: $sw = Newton($opt_1, $max, $n, $eps, $step, $y, $x, $dx, $H, "snx1", "dsnx1", "hesse1"); break; case 2: $sw = Newton($opt_1, $max, $n, $eps, $step, $y, $x, $dx, $H, "snx2", "dsnx2", "hesse2"); break; case 3: $sw = Newton($opt_1, $max, $n, $eps, $step, $y, $x, $dx, $H, "snx3", "dsnx3", "hesse3"); break; } // 結果の出力 if ($sw < 0) { printf(" 収束しませんでした!"); switch ($sw) { case -1: printf("(収束回数)\n"); break; case -2: printf("(1次元最適化の区間)\n"); break; case -3: printf("(黄金分割法)\n"); break; } } else { printf(" 結果="); for ($i1 = 0; $i1 < $n; $i1++) printf("%f ", $x[$i1]); printf(" 最小値=%f 回数=%d\n", $y, $sw); } /********************************************************/ /* Newton法 */ /* opt_1 : =0 : 1次元最適化を行わない */ /* =1 : 1次元最適化を行う */ /* max : 最大繰り返し回数 */ /* n : 次元 */ /* eps : 収束判定条件 */ /* step : きざみ幅 */ /* y : 最小値 */ /* x : x(初期値と答え) */ /* dx : 関数の微分値 */ /* H : Hesse行列の逆行列 */ /* snx : 関数値を計算する関数名 */ /* dsnx : 関数の微分を計算する関数名(符号を変える) */ /* hesse : Hesse行列の逆行列を計算する関数名 */ /* return : >=0 : 正常終了(収束回数) */ /* =-1 : 収束せず */ /* =-2 : 1次元最適化の区間が求まらない */ /* =-3 : 黄金分割法が失敗 */ /********************************************************/ function Newton($opt_1, $max, $n, $eps, $step, &$y, &$x, $dx, $H, $snx, $dsnx, $hesse) { $wk = array($n); $count = 0; $sw = 0; $y1 = $snx(0.0, $x, $dx); while ($count < $max && $sw == 0) { // 傾きの計算 $dsnx($x, $wk); // Hesse行列の逆行列の計算 $sw1 = $hesse($x, $H, $eps); // 収束していない if ($sw1 == 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 = $snx(0.0, $x, $dx); // 関数値の変化が大きい if (abs($y2-$y1) > $eps) $y1 = $y2; // 収束(関数値の変化<eps) else { $sw = $count; $y = $y2; } } // 1次元最適化を行う else { // 区間を決める $sw1 = 0; $f1 = $y1; $sp = $step; $f2 = $snx($sp, $x, $dx); if ($f2 > $f1) $sp = -$step; for ($i1 = 0; $i1 < $max && $sw1 == 0; $i1++) { $f2 = $snx($sp, $x, $dx); if ($f2 > $f1) $sw1 = 1; else { $sp *= 2.0; $f1 = $f2; } } // 区間が求まらない if ($sw1 == 0) $sw = -2; // 区間が求まった else { // 黄金分割法 $k = gold(0.0, $sp, $eps, $y2, $sw1, $max, $x, $dx, $snx); // 黄金分割法が失敗 if ($sw1 < 0) $sw = -3; // 黄金分割法が成功 else { // 新しい点 for ($i1 = 0; $i1 < $n; $i1++) $x[$i1] += $k * $dx[$i1]; // 関数値の変化が大きい if (abs($y1-$y2) > $eps) $y1 = $y2; // 収束(関数値の変化<eps) else { $sw = $count; $y = $y2; } } } } } // 収束(傾き<eps) else { $sw = $count; $y = $y1; } } if ($sw == 0) $sw = -1; return $sw; } /*******************************/ /* Hesse行列の逆行列を計算する */ /*******************************/ // 関数1 function hesse1($x, &$H, $eps) { $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; $sw = Gauss($H, 2, 2, $eps); return $sw; } // 関数2 function hesse2($x, &$H, $eps) { $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; $sw = Gauss($H, 2, 2, $eps); return $sw; } // 関数3 function hesse3($x, &$H, $eps) { $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; $sw = Gauss($H, 2, 2, $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; $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); } /*********************************************/ /* 与えられた点xから,dx方向へk*dxだけ進んだ */ /* 点における関数値を計算する */ /*********************************************/ // 関数1 function snx1($k, $x, $dx) { $w = array(2); $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; return $y; } // 関数2 function snx2($k, $x, $dx) { $w = array(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; return $y; } // 関数3 function snx3($k, $x, $dx) { $w = array(2); $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; return $y; } /********************/ /* 微係数を計算する */ /********************/ // 関数1 function dsnx1($x, &$dx) { $dx[0] = -2.0 * ($x[0] - 1.0); $dx[1] = -2.0 * ($x[1] - 2.0); } // 関数2 function dsnx2($x, &$dx) { $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]); } // 関数3 function dsnx3($x, &$dx) { $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])); } /****************************************************************/ /* 黄金分割法(与えられた方向での最小値) */ /* a,b : 初期区間 a < b */ /* eps : 許容誤差 */ /* val : 関数値 */ /* ind : 計算状況 */ /* >= 0 : 正常終了(収束回数) */ /* = -1 : 収束せず */ /* max : 最大試行回数 */ /* w : 位置 */ /* dw : 傾きの成分 */ /* snx : 関数値を計算する関数の名前 */ /* return : 結果(w+y*dwのy) */ /****************************************************************/ function gold($a, $b, $eps, &$val, &$ind, $max, $w, $dw, $snx) { // 初期設定 $tau = (sqrt(5.0) - 1.0) / 2.0; $x = 0.0; $count = 0; $ind = -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) { $count += 1; if ($f2 > $f1) { if (abs($b-$a) < $eps) { $ind = 0; $x = $x1; $val = $f1; } else { $b = $x2; $x2 = $x1; $x1 = $a + (1.0 - $tau) * ($b - $a); $f2 = $f1; $f1 = $snx($x1, $w, $dw); } } else { if (abs($b-$a) < $eps) { $ind = 0; $x = $x2; $val = $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) { $ind = $count; $fa = $snx($a, $w, $dw); $fb = $snx($b, $w, $dw); if ($fb < $fa) { $a = $b; $fa = $fb; } if ($fa < $f1) { $x = $a; $val = $fa; } } return $x; } ?>
#*****************************/ # Newton法による最小値の計算 */ # coded by Y.Suganuma */ #*****************************/ #******************************************************/ # 線形連立方程式を解く(逆行列を求める) */ # w : 方程式の左辺及び右辺 */ # n : 方程式の数 */ # m : 方程式の右辺の列の数 */ # eps : 正則性を判定する規準 */ # return : =0 : 正常 */ # =1 : 逆行列が存在しない */ #******************************************************/ def Gauss(w, n, m, eps) ind = 0 nm = n + m for i1 in 0 ... n y1 = 0.0 m1 = i1 + 1 m2 = 0 for i2 in i1 ... n y2 = w[i2][i1].abs() if y1 < y2 y1 = y2 m2 = i2 end end if y1 < eps ind = 1 break else for i2 in i1 ... nm y1 = w[i1][i2] w[i1][i2] = w[m2][i2] w[m2][i2] = y1 end y1 = 1.0 / w[i1][i1] for i2 in m1 ... nm w[i1][i2] *= y1 end for i2 in 0 ... n if i2 != i1 for i3 in m1 ... nm w[i2][i3] -= w[i2][i1] * w[i1][i3] end end end end end w[0][nm] = ind return w end #******************************************************/ # 与えられた点xから,dx方向へk*dxだけ進んだ点における */ # 関数値,微係数,及び,Hesse行列の逆行列を計算する */ #******************************************************/ # 関数1 snx1 = Proc.new { |sw, k, x, dx, h, eps| if sw == 0 w = Array.new(2) w[0] = x[0] + k * dx[0] w[1] = x[1] + k * dx[1] x1 = w[0] - 1.0 y1 = w[1] - 2.0 x1 * x1 + y1 * y1 elsif sw == 1 dx[0] = -2.0 * (x[0] - 1.0) dx[1] = -2.0 * (x[1] - 2.0) else h = [[2.0, 0.0, 1.0, 0.0, 0.0], [0.0, 2.0, 0.0, 1.0, 0.0]] Gauss(h, 2, 2, eps) end } # 関数2 snx2 = Proc.new { |sw, k, x, dx, h, eps| if sw == 0 w = Array.new(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] 100.0 * x1 * x1 + y1 * y1 elsif sw == 1 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]) else 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 Gauss(h, 2, 2, eps) end } # 関数3 snx3 = Proc.new { |sw, k, x, dx, h, eps| if sw == 0 w = Array.new(2) 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]) x1 * x1 + y1 * y1 + z1 * z1 elsif sw == 1 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])) else 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 Gauss(h, 2, 2, eps) end } #***************************************************************/ # 黄金分割法(与えられた方向での最小値) */ # a,b : 初期区間 a < b */ # eps : 許容誤差 */ # val : 関数値 */ # ind : 計算状況 */ # >= 0 : 正常終了(収束回数) */ # = -1 : 収束せず */ # max : 最大試行回数 */ # w : 位置 */ # dw : 傾きの成分 */ # h : Hesse行列の逆行列 */ # snx : 関数値を計算する関数の名前 */ # (微分は,その符号を変える) */ # return : 結果(w+y*dwのy) */ #***************************************************************/ def gold(a, b, eps, val, ind, max, w, dw, h, &snx) # 初期設定 tau = (Math.sqrt(5.0) - 1.0) / 2.0 x = 0.0 count = 0 ind[0] = -1 x1 = b - tau * (b - a) x2 = a + tau * (b - a) f1 = snx.call(0, x1, w, dw, h, eps) f2 = snx.call(0, x2, w, dw, h, eps) # 計算 while count < max && ind[0] < 0 count += 1 if f2 > f1 if (b-a).abs() < 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.call(0, x1, w, dw, h, eps) end else if (b-a).abs() < 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.call(0, x2, w, dw, h, eps) end end end # 収束した場合の処理 if ind[0] == 0 ind[0] = count fa = snx.call(0, a, w, dw, h, eps) fb = snx.call(0, b, w, dw, h, eps) if fb < fa a = b fa = fb end if fa < f1 x = a val[0] = fa end end return x end #*******************************************************/ # Newton法 */ # opt_1 : =0 : 1次元最適化を行わない */ # =1 : 1次元最適化を行う */ # max : 最大繰り返し回数 */ # n : 次元 */ # eps : 収束判定条件 */ # step : きざみ幅 */ # y : 最小値 */ # x : x(初期値と答え) */ # dx : 関数の微分値 */ # h : Hesse行列の逆行列 */ # snx : 関数値,その微分,及び,Hesse行列の逆行列 */ # return : >=0 : 正常終了(収束回数) */ # =-1 : 収束せず */ # =-2 : 1次元最適化の区間が求まらない */ # =-3 : 黄金分割法が失敗 */ #*******************************************************/ def Newton(opt_1, max, n, eps, step, y, x, dx, h, &snx) sw1 = Array.new(n) y2 = Array.new(n) wk = Array.new(n) for i1 in 0 ... n wk[i1] = 0.0 end count = 0 sw = 0 y1 = snx.call(0, 0.0, x, dx, h, eps) while count < max && sw == 0 # 傾きの計算 snx.call(1, 0.0, x, wk, h, eps) # Hesse行列の逆行列の計算 h = snx.call(2, 0.0, x, wk, h, eps) sw1[0] = h[0][2*n] # もっと良い方法はあるか? # 収束していない if sw1[0] == 0 # 方向の計算 count += 1 for i1 in 0 ... n dx[i1] = 0.0 for i2 in 0 ... n dx[i1] += h[i1][n+i2] * wk[i2] end end # 1次元最適化を行わない if opt_1 == 0 # 新しい点 for i1 in 0 ... n x[i1] += dx[i1] end # 新しい関数値 y2[0] = snx.call(0, 0.0, x, dx, h, eps) # 関数値の変化が大きい if (y2[0]-y1).abs() > eps y1 = y2[0] # 収束(関数値の変化<eps) else sw = count y[0] = y2[0] end # 1次元最適化を行う else # 区間を決める sw1[0] = 0 f1 = y1 sp = step f2 = snx.call(0, sp, x, dx, h, eps) if f2 > f1 sp = -step end for i1 in 0 ... max f2 = snx.call(0, sp, x, dx, h, eps) if f2 > f1 sw1[0] = 1 break else sp *= 2.0 f1 = f2 end end # 区間が求まらない if sw1[0] == 0 sw = -2 # 区間が求まった else # 黄金分割法 k = gold(0.0, sp, eps, y2, sw1, max, x, dx, h, &snx) # 黄金分割法が失敗 if sw1[0] < 0 sw = -3 # 黄金分割法が成功 else # 新しい点 for i1 in 0 ... n x[i1] += k * dx[i1] end # 関数値の変化が大きい if (y1-y2[0]).abs() > eps y1 = y2[0] # 収束(関数値の変化<eps) else sw = count y[0] = y2[0] end end end end # 収束(傾き<eps) else sw = count y[0] = y1 end end if sw == 0 sw = -1 end return sw end # データの入力 str = gets() a = str.split(" ") fun = Integer(a[1]) n = Integer(a[3]) max = Integer(a[5]) opt_1 = Integer(a[7]) str = gets() a = str.split(" ") eps = Float(a[1]) step = Float(a[3]) x = Array.new(n) dx = Array.new(n) y = Array.new(1) sw = 0 str = gets() a = str.split(" ") h = Array.new(n) for i1 in 0 ... n x[i1] = Float(a[i1+1]) dx[i1] = 0.0 h[i1] = Array.new(2*n+1) end # 実行 case fun when 1 sw = Newton(opt_1, max, n, eps, step, y, x, dx, h, &snx1) when 2 sw = Newton(opt_1, max, n, eps, step, y, x, dx, h, &snx2) when 3 sw = Newton(opt_1, max, n, eps, step, y, x, dx, h, &snx3) end # 結果の出力 if sw < 0 printf(" 収束しませんでした!") case sw when -1 printf("(収束回数)\n") when -2 printf("(1次元最適化の区間)\n") when -3 printf("(黄金分割法)\n") end else printf(" 結果=") for i1 in 0 ... n printf("%f ", x[i1]) end printf(" 最小値=%f 回数=%d\n", y[0], sw) end
# -*- coding: UTF-8 -*- import numpy as np import sys from math import * ############################################ # 黄金分割法(与えられた方向での最小値) # a,b : 初期区間 a < b # eps : 許容誤差 # val : 関数値 # ind : 計算状況 # >= 0 : 正常終了(収束回数) # = -1 : 収束せず # max : 最大試行回数 # w : 位置 # dw : 傾きの成分 # snx : 関数値を計算する関数の名前 # return : 結果(w+y*dwのy) # coded by Y.Suganuma ############################################ def gold(a, b, eps, val, ind, max, w, dw, snx) : # 初期設定 tau = (sqrt(5.0) - 1.0) / 2.0 x = 0.0 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 and ind[0] < 0 : count += 1 if f2 > f1 : if 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 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 : 逆行列が存在しない # coded by Y.Suganuma ############################################ def gauss(w, n, m, eps) : nm = n + m ind = 0 for i1 in range(0, n) : y1 = 0.0 m1 = i1 + 1 m2 = 0 # ピボット要素の選択 for i2 in range(i1, n) : y2 = abs(w[i2][i1]) if y1 < y2 : y1 = y2 m2 = i2 # 逆行列が存在しない if y1 < eps : ind = 1 break # 逆行列が存在する else : # 行の入れ替え for i2 in range(i1, nm) : y1 = w[i1][i2] w[i1][i2] = w[m2][i2] w[m2][i2] = y1 # 掃き出し操作 y1 = 1.0 / w[i1][i1] for i2 in range(m1, nm) : w[i1][i2] *= y1 for i2 in range(0, n) : if i2 != i1 : for i3 in range(m1, nm) : w[i2][i3] -= (w[i2][i1] * w[i1][i3]) return ind ############################################ # Newton法 # opt_1 : =0 : 1次元最適化を行わない # =1 : 1次元最適化を行う # max : 最大繰り返し回数 # n : 次元 # eps : 収束判定条件 # step : きざみ幅 # y : 最小値 # x : x(初期値と答え) # dx : 関数の微分値 # H : Hesse行列の逆行列 # snx : 関数値を計算する関数名 # dsnx : 関数の微分を計算する関数名(符号を変える) # hesse : Hesse行列の逆行列を計算する関数名 # return : >=0 : 正常終了(収束回数) # =-1 : 収束せず # =-2 : 1次元最適化の区間が求まらない # =-3 : 黄金分割法が失敗 # coded by Y.Suganuma ############################################ def Newton(opt_1, max, n, eps, step, y, x, dx, H, snx, dsnx, hesse) : wk = np.empty(n, np.float) count = 0 sw = 0 y2 = np.empty(1, np.float) sw1 = np.empty(1, np.int) y1 = snx(0.0, x, dx) while count < max and sw == 0 : # 傾きの計算 dsnx(x, wk) # Hesse行列の逆行列の計算 sw1[0] = hesse(x, H, eps) # 収束していない if sw1[0] == 0 : # 方向の計算 count += 1 for i1 in range(0, n) : dx[i1] = 0.0 for i2 in range(0, n) : dx[i1] += H[i1][n+i2] * wk[i2] # 1次元最適化を行わない if opt_1 == 0 : # 新しい点 for i1 in range(0, n) : x[i1] += dx[i1] # 新しい関数値 y2[0] = snx(0.0, x, dx) # 関数値の変化が大きい if abs(y2[0]-y1) > eps : y1 = y2[0] # 収束(関数値の変化<eps) else : sw = count y[0] = y2[0] # 1次元最適化を行う else : # 区間を決める sw1[0] = 0 f1 = y1 sp = step f2 = snx(sp, x, dx) if f2 > f1 : sp = -step for i1 in range(0, max) : f2 = snx(sp, x, dx) if f2 > f1 : sw1[0] = 1 break else : sp *= 2.0 f1 = f2 # 区間が求まらない if sw1[0] == 0 : sw = -2 # 区間が求まった else : # 黄金分割法 k = gold(0.0, sp, eps, y2, sw1, max, x, dx, snx) # 黄金分割法が失敗 if sw1[0] < 0 : sw = -3 # 黄金分割法が成功 else : # 新しい点 for i1 in range(0, n) : x[i1] += k * dx[i1] # 関数値の変化が大きい if abs(y1-y2[0]) > eps : y1 = y2[0] # 収束(関数値の変化<eps) else : sw = count y[0] = y2[0] # 収束(傾き<eps) else : sw = count y[0] = y1 if sw == 0 : sw = -1 return sw ############################################ # Newton法による最小値の計算 # coded by Y.Suganuma ############################################ ############################################ # 与えられた点xから,dx方向へk*dxだけ進んだ # 点における関数値を計算する ############################################ # 関数1 def snx1(k, x, dx) : w = np.empty(2, np.float) 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 return y # 関数2 def snx2(k, x, dx) : w = np.empty(2, np.float) 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 return y # 関数3 def snx3(k, x, dx) : w = np.empty(2, np.float) 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 return y ############################################ # 微係数を計算する ############################################ # 関数1 def dsnx1(x, dx) : dx[0] = -2.0 * (x[0] - 1.0) dx[1] = -2.0 * (x[1] - 2.0) # 関数2 def dsnx2(x, dx) : 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]) # 関数3 def dsnx3(x, dx) : 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])) ############################################ # Hesse行列の逆行列を計算する */ ############################################ # 関数1 def hesse1(x, H, eps) : 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 sw = gauss(H, 2, 2, eps) return sw # 関数2 def hesse2(x, H, eps) : 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 sw = gauss(H, 2, 2, eps) return sw # 関数3 def hesse3(x, H, eps) : 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 sw = gauss(H, 2, 2, eps) return sw # データの入力 sw = 0 line = sys.stdin.readline() s = line.split() fun = int(s[1]) n = int(s[3]) max = int(s[5]) opt_1 = int(s[7]) line = sys.stdin.readline() s = line.split() eps = float(s[1]) step = float(s[3]) x = np.empty(n, np.float) dx = np.empty(n, np.float) H = np.empty((n, 2*n), np.float) y = np.empty(1, np.float) line = sys.stdin.readline() s = line.split() for i1 in range(0, n) : x[i1] = float(s[i1+1]) # 実行 if fun == 1 : sw = Newton(opt_1, max, n, eps, step, y, x, dx, H, snx1, dsnx1, hesse1) elif fun == 2 : sw = Newton(opt_1, max, n, eps, step, y, x, dx, H, snx2, dsnx2, hesse2) else : sw = Newton(opt_1, max, n, eps, step, y, x, dx, H, snx3, dsnx3, hesse3) # 結果の出力 if sw < 0 : print(" 収束しませんでした!", end="") if sw == -1 : print("(収束回数)") elif sw == -2 : print("(1次元最適化の区間)") else : print("(黄金分割法)") else : print(" 結果=", end="") for i1 in range(0, n) : print(str(x[i1]) + " ", end="") print(" 最小値=" + str(y[0]) + " 回数=" + str(sw))
/******************************/ /* Newton法による最小値の計算 */ /* coded by Y.Suganuma */ /******************************/ using System; class Program { static void Main() { Test1 ts = new Test1(); } } class Test1 { public Test1() { // データの入力 // 1 行目 string[] str = Console.ReadLine().Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); int fun = int.Parse(str[1]); int n = int.Parse(str[3]); int max = int.Parse(str[5]); int opt_1 = int.Parse(str[7]); // 2 行目 str = Console.ReadLine().Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); double eps = double.Parse(str[1]); double step = double.Parse(str[3]); // 3 行目 double[][] H = new double [n][]; double[] x = new double [n]; double[] dx = new double [n]; double y = 0.0; str = Console.ReadLine().Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries); for (int i1 = 0; i1 < n; i1++) { x[i1] = double.Parse(str[i1+1]); H[i1] = new double [2*n]; } // 実行 int sw; if (fun == 1) sw = Newton(opt_1, max, n, eps, step, ref y, x, dx, H, snx1, dsnx1, hesse1); else if (fun == 2) sw = Newton(opt_1, max, n, eps, step, ref y, x, dx, H, snx2, dsnx2, hesse2); else sw = Newton(opt_1, max, n, eps, step, ref y, x, dx, H, snx3, dsnx3, hesse3); // 結果の出力 if (sw < 0) { Console.Write(" 収束しませんでした!"); switch (sw) { case -1: Console.WriteLine("(収束回数)"); break; case -2: Console.WriteLine("(1次元最適化の区間)"); break; case -3: Console.WriteLine("(黄金分割法)"); break; } } else { Console.Write(" 結果="); for (int i1 = 0; i1 < n; i1++) Console.Write(x[i1] + " "); Console.WriteLine(" 最小値=" + y + " 回数=" + sw); } } // 関数1の値の計算 double snx1(double k, double[] x, double[] dx) { double[] w = new double [2]; w[0] = x[0] + k * dx[0]; w[1] = x[1] + k * dx[1]; double x1 = w[0] - 1.0; double y1 = w[1] - 2.0; double y = x1 * x1 + y1 * y1; return y; } // 関数2の値の計算 double snx2(double k, double[] x, double[] dx) { double[] w = new double [2]; w[0] = x[0] + k * dx[0]; w[1] = x[1] + k * dx[1]; double x1 = w[1] - w[0] * w[0]; double y1 = 1.0 - w[0]; double y = 100.0 * x1 * x1 + y1 * y1; return y; } // 関数3の値の計算 double snx3(double k, double[] x, double[] dx) { double[] w = new double [2]; w[0] = x[0] + k * dx[0]; w[1] = x[1] + k * dx[1]; double x1 = 1.5 - w[0] * (1.0 - w[1]); double y1 = 2.25 - w[0] * (1.0 - w[1] * w[1]); double z1 = 2.625 - w[0] * (1.0 - w[1] * w[1] * w[1]); double y = x1 * x1 + y1 * y1 + z1 * z1; return y; } // 関数1の微分 int dsnx1(double[] x, double[] dx) { dx[0] = -2.0 * (x[0] - 1.0); dx[1] = -2.0 * (x[1] - 2.0); return 0; } // 関数2の微分 int dsnx2(double[] x, double[] dx) { 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]); return 0; } // 関数3の微分 int dsnx3(double[] x, double[] dx) { 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])); return 0; } // Hesse行列の逆行列1 int hesse1(double[] x, double[][] H, double eps) { 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; int sw1 = gauss(H, 2, 2, eps); return sw1; } // Hesse行列の逆行列2 int hesse2(double[] x, double[][] H, double eps) { 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; int sw1 = gauss(H, 2, 2, eps); return sw1; } // Hesse行列の逆行列3 int hesse3(double[] x, double[][] H, double eps) { double x1 = 1.0 - x[1]; double x2 = 1.0 - x[1] * x[1]; double 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; int sw1 = gauss(H, 2, 2, eps); return sw1; } /********************************************************/ /* Newton法 */ /* opt_1 : =0 : 1次元最適化を行わない */ /* =1 : 1次元最適化を行う */ /* max : 最大繰り返し回数 */ /* n : 次元 */ /* eps : 収束判定条件 */ /* step : きざみ幅 */ /* y : 最小値 */ /* x : x(初期値と答え) */ /* dx : 関数の微分値 */ /* H : Hesse行列の逆行列 */ /* fn : 関数値を計算する関数 */ /* dfn : 関数の微分値を計算する関数 */ /* hesse : Hesse行列の逆行列を計算する関数 */ /* return : >=0 : 正常終了(収束回数) */ /* =-1 : 収束せず */ /* =-2 : 1次元最適化の区間が求まらない */ /* =-3 : 黄金分割法が失敗 */ /********************************************************/ int Newton(int opt_1, int max, int n, double eps, double step, ref double y, double[] x, double[] dx, double[][] H, Func<double, double[], double[], double> fn, Func<double[], double[], int> dfn, Func<double[], double[][], double, int> hesse) { double[] wk = new double [n]; double y1 = fn(0.0, x, dx); double y2 = 0.0; int count = 0; int sw = 0; while (count < max && sw == 0) { // 傾きの計算 dfn(x, wk); // Hesse行列の逆行列の計算 int sw1 = hesse(x, H, eps); // 収束していない if (sw1 == 0) { // 方向の計算 count++; for (int i1 = 0; i1 < n; i1++) { dx[i1] = 0.0; for (int i2 = 0; i2 < n; i2++) dx[i1] += H[i1][n+i2] * wk[i2]; } // 1次元最適化を行わない if (opt_1 == 0) { // 新しい点 for (int i1 = 0; i1 < n; i1++) x[i1] += dx[i1]; // 新しい関数値 y2 = fn(0.0, x, dx); // 関数値の変化が大きい if (Math.Abs(y2-y1) > eps) y1 = y2; // 収束(関数値の変化<eps) else { sw = count; y = y2; } } // 1次元最適化を行う else { // 区間を決める sw1 = 0; double f1 = y1; double sp = step; double f2 = fn(sp, x, dx); if (f2 > f1) sp = -step; for (int i1 = 0; i1 < max && sw1 == 0; i1++) { f2 = fn(sp, x, dx); if (f2 > f1) sw1 = 1; else { sp *= 2.0; f1 = f2; } } // 区間が求まらない if (sw1 == 0) sw = -2; // 区間が求まった else { // 黄金分割法 double k = gold(0.0, sp, eps, ref y2, ref sw1, max, x, dx, fn); // 黄金分割法が失敗 if (sw1 < 0) sw = -3; // 黄金分割法が成功 else { // 新しい点 for (int i1 = 0; i1 < n; i1++) x[i1] += k * dx[i1]; // 関数値の変化が大きい if (Math.Abs(y1-y2) > eps) y1 = y2; // 収束(関数値の変化<eps) else { sw = count; y = y2; } } } } } // 収束(傾き<eps) else { sw = count; y = y1; } } if (sw == 0) sw = -1; return sw; } /******************************************/ /* 黄金分割法(与えられた方向での最小値) */ /* a,b : 初期区間 a < b */ /* eps : 許容誤差 */ /* val : 関数値 */ /* ind : 計算状況 */ /* >= 0 : 正常終了(収束回数) */ /* = -1 : 収束せず */ /* max : 最大試行回数 */ /* w : 位置 */ /* dw : 傾きの成分 */ /* fn : 関数値を計算する関数 */ /* return : 結果(w+y*dwのy) */ /******************************************/ double gold(double a, double b, double eps, ref double val, ref int ind, int max, double[] w, double[] dw, Func<double, double[], double[], double> fn) { // 初期設定 double tau = (Math.Sqrt(5.0) - 1.0) / 2.0, x = 0.0; double x1 = b - tau * (b - a); double x2 = a + tau * (b - a); double f1 = fn(x1, w, dw); double f2 = fn(x2, w, dw); int count = 0; ind = -1; // 計算 while (count < max && ind < 0) { count += 1; if (f2 > f1) { if (Math.Abs(b-a) < eps) { ind = 0; x = x1; val = f1; } else { b = x2; x2 = x1; x1 = a + (1.0 - tau) * (b - a); f2 = f1; f1 = fn(x1, w, dw); } } else { if (Math.Abs(b-a) < eps) { ind = 0; x = x2; val = f2; f1 = f2; } else { a = x1; x1 = x2; x2 = b - (1.0 - tau) * (b - a); f1 = f2; f2 = fn(x2, w, dw); } } } // 収束した場合の処理 if (ind == 0) { ind = count; double fa = fn(a, w, dw); double fb = fn(b, w, dw); if (fb < fa) { a = b; fa = fb; } if (fa < f1) { x = a; val = fa; } } return x; } /*******************************************************/ /* 線形連立方程式を解く(逆行列を求める) */ /* w : 方程式の左辺及び右辺 */ /* n : 方程式の数 */ /* m : 方程式の右辺の列の数 */ /* eps : 逆行列の存在を判定する規準 */ /* return : =0 : 正常 */ /* =1 : 逆行列が存在しない */ /*******************************************************/ int gauss(double[][] w, int n, int m, double eps) { int ind = 0; int nm = n + m; for (int i1 = 0; i1 < n && ind == 0; i1++) { double y1 = .0; int m1 = i1 + 1; int m2 = 0; // ピボット要素の選択 for (int i2 = i1; i2 < n; i2++) { double y2 = Math.Abs(w[i2][i1]); if (y1 < y2) { y1 = y2; m2 = i2; } } // 逆行列が存在しない if (y1 < eps) ind = 1; // 逆行列が存在する else { // 行の入れ替え for (int 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 (int i2 = m1; i2 < nm; i2++) w[i1][i2] *= y1; for (int i2 = 0; i2 < n; i2++) { if (i2 != i1) { for (int i3 = m1; i3 < nm; i3++) w[i2][i3] -= w[i2][i1] * w[i1][i3]; } } } } return ind; } }
'''''''''''''''''''''''''''''' ' Newton法による最小値の計算 ' ' coded by Y.Suganuma ' '''''''''''''''''''''''''''''' Imports System.Text.RegularExpressions Module Test Sub Main() ' データの入力 ' 1 行目 Dim MS As Regex = New Regex("\s+") Dim str() As String = MS.Split(Console.ReadLine().Trim()) Dim fun As Integer = Integer.Parse(str(1)) Dim n As Integer = Integer.Parse(str(3)) Dim max As Integer = Integer.Parse(str(5)) Dim opt_1 As Integer = Integer.Parse(str(7)) ' 2 行目 str = MS.Split(Console.ReadLine().Trim()) Dim eps As Double = Double.Parse(str(1)) Dim step1 As Double = Double.Parse(str(3)) ' 3 行目 Dim H(n,2*n) As Double Dim x(n) As Double Dim dx(n) As Double Dim y As Double = 0.0 str = MS.Split(Console.ReadLine().Trim()) For i1 As Integer = 0 To n-1 x(i1) = Double.Parse(str(i1+1)) Next ' 関数1の値の計算(ラムダ式) Dim snx1 = Function(k0, x0, dx0) As Double Dim w(2) As Double w(0) = x0(0) + k0 * dx0(0) w(1) = x0(1) + k0 * dx0(1) Dim x1 As Double = w(0) - 1.0 Dim y1 As Double = w(1) - 2.0 Dim y0 As Double = x1 * x1 + y1 * y1 Return y0 End Function ' 関数2の値の計算(ラムダ式) Dim snx2 = Function(k0, x0, dx0) As Double Dim w(2) As Double w(0) = x0(0) + k0 * dx0(0) w(1) = x0(1) + k0 * dx0(1) Dim x1 As Double = w(1) - w(0) * w(0) Dim y1 As Double = 1.0 - w(0) Dim y0 As Double = 100.0 * x1 * x1 + y1 * y1 Return y0 End Function ' 関数3の値の計算(ラムダ式) Dim snx3 = Function(k0, x0, dx0) As Double Dim w(2) As Double w(0) = x0(0) + k0 * dx0(0) w(1) = x0(1) + k0 * dx0(1) Dim x1 As Double = 1.5 - w(0) * (1.0 - w(1)) Dim y1 As Double = 2.25 - w(0) * (1.0 - w(1) * w(1)) Dim z1 As Double = 2.625 - w(0) * (1.0 - w(1) * w(1) * w(1)) Dim y0 As Double = x1 * x1 + y1 * y1 + z1 * z1 Return y0 End Function ' 関数1の微分(ラムダ式) Dim dsnx1 = Function(x0, dx0) As Integer dx0(0) = -2.0 * (x0(0) - 1.0) dx0(1) = -2.0 * (x0(1) - 2.0) Return 0 End Function ' 関数2の微分(ラムダ式) Dim dsnx2 = Function(x0, dx0) As Integer dx0(0) = 400.0 * x0(0) * (x0(1) - x0(0) * x0(0)) + 2.0 * (1.0 - x0(0)) dx0(1) = -200.0 * (x0(1) - x0(0) * x0(0)) Return 0 End Function ' 関数3の微分(ラムダ式) Dim dsnx3 = Function(x0, dx0) As Integer dx0(0) = 2.0 * (1.0 - x0(1)) * (1.5 - x0(0) * (1.0 - x0(1))) + 2.0 * (1.0 - x0(1) * x0(1)) * (2.25 - x0(0) * (1.0 - x0(1) * x0(1))) + 2.0 * (1.0 - x0(1) * x0(1) * x0(1)) * (2.625 - x0(0) * (1.0 - x0(1) * x0(1) * x0(1))) dx0(1) = -2.0 * x0(0) * (1.5 - x0(0) * (1.0 - x0(1))) - 4.0 * x0(0) * x0(1) * (2.25 - x0(0) * (1.0 - x0(1) * x0(1))) - 6.0 * x0(0) * x0(1) * x0(1) * (2.625 - x0(0) * (1.0 - x0(1) * x0(1) * x0(1))) Return 0 End Function ' Hesse行列の逆行列1(ラムダ式) Dim hesse1 = Function(x0, H0, eps0) H0(0,0) = 2.0 H0(0,1) = 0.0 H0(1,0) = 0.0 H0(1,1) = 2.0 H0(0,2) = 1.0 H0(0,3) = 0.0 H0(1,2) = 0.0 H0(1,3) = 1.0 Dim sw1 As Integer = gauss(H0, 2, 2, eps0) Return sw1 End Function ' Hesse行列の逆行列2(ラムダ式) Dim hesse2 = Function(x0, H0, eps0) H0(0,0) = 400.0 * (3.0 * x0(0) * x0(0) - x0(1)) + 2.0 H0(0,1) = -400.0 * x0(0) H0(1,0) = H0(0,1) H0(1,1) = 200.0 H0(0,2) = 1.0 H0(0,3) = 0.0 H0(1,2) = 0.0 H0(1,3) = 1.0 Dim sw1 As Integer = gauss(H0, 2, 2, eps0) Return sw1 End Function ' Hesse行列の逆行列3(ラムダ式) Dim hesse3 = Function(x0, H0, eps0) Dim x1 As Double = 1.0 - x0(1) Dim x2 As Double = 1.0 - x0(1) * x0(1) Dim x3 As Double = 1.0 - x0(1) * x0(1) * x0(1) H0(0,0) = 2.0 * x1 * x1 + 2.0 * x2 * x2 + 2.0 * x3 * x3 H0(0,1) = 2.0 * (1.5 - x0(0) * x1) - 2.0 * x0(0) * x1 + 4.0 * x0(1) * (2.25 - x0(0) * x2) - 4.0 * x0(0) * x0(1) * x2 + 6.0 * x0(1) * x0(1) * (2.625 - x0(0) * x3) - 6.0 * x0(0) * x0(1) * x0(1) * x3 H0(1,0) = H0(0,1) H0(1,1) = 2.0 * x0(0) * x0(0) + 4.0 * x0(0) * (2.25 - x0(0) * x2) + 8.0 * x0(0) * x0(0) * x0(1) * x0(1) + 12.0 * x0(0) * x0(1) * (2.625 - x0(0) * x3) + 18.0 * x0(0) * x0(0) * x0(1) * x0(1) * x0(1) * x0(1) H0(0,2) = 1.0 H0(0,3) = 0.0 H0(1,2) = 0.0 H0(1,3) = 1.0 Dim sw1 As Integer = gauss(H0, 2, 2, eps0) Return sw1 End Function ' 実行 Dim sw As Integer If fun = 1 sw = Newton(opt_1, max, n, eps, step1, y, x, dx, H, snx1, dsnx1, hesse1) ElseIf fun = 2 sw = Newton(opt_1, max, n, eps, step1, y, x, dx, H, snx2, dsnx2, hesse2) Else sw = Newton(opt_1, max, n, eps, step1, y, x, dx, H, snx3, dsnx3, hesse3) End If ' 結果の出力 If sw < 0 Console.Write(" 収束しませんでした!") If sw = -1 Console.WriteLine("(収束回数)") ElseIf sw = -2 Console.WriteLine("(1次元最適化の区間)") ElseIf sw = -3 Console.WriteLine("(黄金分割法)") End If Else Console.Write(" 結果=") For i1 As Integer = 0 To n-1 Console.Write(x(i1) & " ") Next Console.WriteLine(" 最小値=" & y & " 回数=" & sw) End If End Sub '''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Newton法 ' ' opt_1 : =0 : 1次元最適化を行わない ' ' =1 : 1次元最適化を行う ' ' max : 最大繰り返し回数 ' ' n : 次元 ' ' eps : 収束判定条件 ' ' step1 : きざみ幅 ' ' y : 最小値 ' ' x : x(初期値と答え) ' ' dx : 関数の微分値 ' ' H : Hesse行列の逆行列 ' ' fn : 関数値を計算する関数 ' ' dfn : 関数の微分値を計算する関数 ' ' hesse : Hesse行列の逆行列を計算する関数 ' ' return : >=0 : 正常終了(収束回数) ' ' =-1 : 収束せず ' ' =-2 : 1次元最適化の区間が求まらない ' ' =-3 : 黄金分割法が失敗 ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''' Function Newton(opt_1 As Integer, max As Integer, n As Integer, eps As Double, step1 As Double, ByRef y As Double, x() As Double, dx() As Double, H(,) As Double, fn As Func(Of Double, Double(), Double(), Double), dfn As Func(Of Double(), Double(), Integer), hesse As Func(Of Double(), Double(,), Double, Integer)) Dim wk(n) As Double Dim y1 As Double = fn(0.0, x, dx) Dim y2 As Double = 0.0 Dim count As Integer = 0 Dim sw As Integer = 0 Do while count < max and sw = 0 ' 傾きの計算 dfn(x, wk) ' Hesse行列の逆行列の計算 Dim sw1 As Integer = hesse(x, H, eps) ' 収束していない If sw1 = 0 ' 方向の計算 count += 1 For i1 As Integer = 0 To n-1 dx(i1) = 0.0 For i2 As Integer = 0 To n-1 dx(i1) += H(i1,n+i2) * wk(i2) Next Next ' 1次元最適化を行わない If opt_1 = 0 ' 新しい点 For i1 As Integer = 0 To n-1 x(i1) += dx(i1) Next ' 新しい関数値 y2 = fn(0.0, x, dx) ' 関数値の変化が大きい If Math.Abs(y2-y1) > eps y1 = y2 ' 収束(関数値の変化<eps) Else sw = count y = y2 End If ' 1次元最適化を行う Else ' 区間を決める sw1 = 0 Dim f1 As Double = y1 Dim sp As Double = step1 Dim f2 As Double = fn(sp, x, dx) If f2 > f1 sp = -step1 End If Dim ii As Integer = 0 Do While ii < max and sw1 = 0 f2 = fn(sp, x, dx) If f2 > f1 sw1 = 1 Else sp *= 2.0 f1 = f2 End If ii += 1 Loop ' 区間が求まらない If sw1 = 0 sw = -2 ' 区間が求まった Else ' 黄金分割法 Dim k As Double = gold(0.0, sp, eps, y2, sw1, max, x, dx, fn) ' 黄金分割法が失敗 If sw1 < 0 sw = -3 ' 黄金分割法が成功 Else ' 新しい点 For i1 As Integer = 0 To n-1 x(i1) += k * dx(i1) Next ' 関数値の変化が大きい If Math.Abs(y1-y2) > eps y1 = y2 ' 収束(関数値の変化<eps) Else sw = count y = y2 End If End If End If End If ' 収束(傾き<eps) Else sw = count y = y1 End If Loop If sw = 0 sw = -1 End If Return sw End Function '''''''''''''''''''''''''''''''''''''''''' ' 黄金分割法(与えられた方向での最小値) ' ' a,b : 初期区間 a < b ' ' eps : 許容誤差 ' ' val : 関数値 ' ' ind : 計算状況 ' ' >= 0 : 正常終了(収束回数) ' ' = -1 : 収束せず ' ' max : 最大試行回数 ' ' w : 位置 ' ' dw : 傾きの成分 ' ' fn : 関数値を計算する関数 ' ' return : 結果(w+y*dwのy) ' '''''''''''''''''''''''''''''''''''''''''' Function gold(a As Double, b As Double, eps As Double, ByRef val As Double, ByRef ind As Integer, max As Integer, w() As Double, dw() As Double, fn As Func(Of Double, Double(), Double(), Double)) ' 初期設定 Dim tau As Double = (Math.Sqrt(5.0) - 1.0) / 2.0, x = 0.0 Dim x1 As Double = b - tau * (b - a) Dim x2 As Double = a + tau * (b - a) Dim f1 As Double = fn(x1, w, dw) Dim f2 As Double = fn(x2, w, dw) Dim count As Integer = 0 ind = -1 ' 計算 Do While count < max and ind < 0 count += 1 If f2 > f1 If Math.Abs(b-a) < eps ind = 0 x = x1 val = f1 Else b = x2 x2 = x1 x1 = a + (1.0 - tau) * (b - a) f2 = f1 f1 = fn(x1, w, dw) End If Else If Math.Abs(b-a) < eps ind = 0 x = x2 val = f2 f1 = f2 Else a = x1 x1 = x2 x2 = b - (1.0 - tau) * (b - a) f1 = f2 f2 = fn(x2, w, dw) End If End If Loop ' 収束した場合の処理 If ind = 0 ind = count Dim fa As Double = fn(a, w, dw) Dim fb As Double = fn(b, w, dw) If fb < fa a = b fa = fb End If If fa < f1 x = a val = fa End If End If Return x End Function '''''''''''''''''''''''''''''''''''''''''' ' 線形連立方程式を解く(逆行列を求める) ' ' w : 方程式の左辺及び右辺 ' ' n : 方程式の数 ' ' m : 方程式の右辺の列の数 ' ' eps : 逆行列の存在を判定する規準 ' ' return : =0 : 正常 ' ' =1 : 逆行列が存在しない ' '''''''''''''''''''''''''''''''''''''''''' Function gauss(w(,) As Double, n As Integer, m As Integer, eps As Double) As Integer Dim ind As Integer = 0 Dim nm As Integer = n + m Dim i1 As Integer = 0 Do While i1 < n and ind = 0 Dim y1 As Double = 0.0 Dim m1 As Integer = i1 + 1 Dim m2 As Integer = 0 ' ピボット要素の選択 For i2 As Integer = i1 To n-1 Dim y2 As Double = Math.Abs(w(i2,i1)) If y1 < y2 y1 = y2 m2 = i2 End If Next ' 逆行列が存在しない If y1 < eps ind = 1 ' 逆行列が存在する Else ' 行の入れ替え For i2 As Integer = i1 To nm-1 y1 = w(i1,i2) w(i1,i2) = w(m2,i2) w(m2,i2) = y1 Next ' 掃き出し操作 y1 = 1.0 / w(i1,i1) For i2 As Integer = m1 To nm-1 w(i1,i2) *= y1 Next For i2 As Integer = 0 To n-1 If i2 <> i1 For i3 As Integer = m1 To nm-1 w(i2,i3) -= w(i2,i1) * w(i1,i3) Next End If Next End If i1 += 1 Loop Return ind End Function End Module
情報学部 | 菅沼ホーム | 目次 | 索引 |