最適化(シンプレックス法)

データ例とプログラム

-------------------------i_data-------------------------

					// 関数 a
関数 1 変数の数 2 最大試行回数 1000 初期値設定係数 1.0
許容誤差 1.0e-20 縮小比率 0.7 拡大比率 1.5
初期値 0.0 0.0
					// 関数 b
関数 2 変数の数 2 最大試行回数 1000 初期値設定係数 1.0
許容誤差 1.0e-20 縮小比率 0.7 拡大比率 1.5
初期値 0.0 0.0
					// 関数 c
関数 3 変数の数 2 最大試行回数 1000 初期値設定係数 1.0
許容誤差 1.0e-20 縮小比率 0.7 拡大比率 1.5
初期値 0.0 0.0

-------------------------プログラム-------------------------

/**************************************/
/* シンプレックス法による最小値の計算 */
/*      coded by Y.Suganuma           */
/**************************************/
import java.io.*;
import java.util.StringTokenizer;

public class Test {
	public static void main(String args[]) throws IOException
	{
		double eps, r1, r2, x[], y[], k;
		int fun, i1, max, n, sw = 0;
		StringTokenizer str;
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
					// データの入力
		str = new StringTokenizer(in.readLine(), " ");
		str.nextToken();
		fun = Integer.parseInt(str.nextToken());
		str.nextToken();
		n = Integer.parseInt(str.nextToken());
		str.nextToken();
		max = Integer.parseInt(str.nextToken());
		str.nextToken();
		k = Double.parseDouble(str.nextToken());
		x = new double [n];
		y  = new double [1];

		str = new StringTokenizer(in.readLine(), " ");
		str.nextToken();
		eps = Double.parseDouble(str.nextToken());
		str.nextToken();
		r1 = Double.parseDouble(str.nextToken());
		str.nextToken();
		r2 = Double.parseDouble(str.nextToken());

		str = new StringTokenizer(in.readLine(), " ");
		str.nextToken();
		for (i1 = 0; i1 < n; i1++) {
			x[i1] = Double.parseDouble(str.nextToken());
		}
					// 実行
		Kansu kn = new Kansu(fun);
		sw = kn.simplex(max, n, k, eps, r1, r2, y, x);
					// 結果の出力
		if (sw < 0)
			System.out.print("   収束しませんでした!");
		else {
			System.out.print("   結果=");
			for (i1 = 0; i1 < n; i1++)
				System.out.print(x[i1] + " ");
			System.out.println(" 最小値=" + y[0] + "  回数=" + sw);
		}
	}
}

/**********/
/* 関数値 */
/**********/
class Kansu extends Simplex
{
	private int sw;
					// コンストラクタ
	Kansu (int s) {sw = s;}
					// 関数値の計算
	double snx(double x[])
	{
		double x1, y1, z1, y = 0.0;

		switch (sw) {
			case 1:
				x1 = x[0] - 1.0;
				y1 = x[1] - 2.0;
				y  = x1 * x1 + y1 * y1;
				break;
			case 2:
				x1 = x[1] - x[0] * x[0];
				y1 = 1.0 - x[0];
				y  = 100.0 * x1 * x1 + y1 * y1;
				break;
			case 3:
				x1 = 1.5 - x[0] * (1.0 - x[1]);
				y1 = 2.25 - x[0] * (1.0 - x[1] * x[1]);
				z1 = 2.625 - x[0] * (1.0 - x[1] * x[1] * x[1]);
				y  = x1 * x1 + y1 * y1 + z1 * z1;
				break;
		}

		return y;
	}
}

abstract class Simplex {

	/******************************************/
	/* シンプレックス法                       */
	/*      max : 最大繰り返し回数            */
	/*      n : 次元                          */
	/*      k : 初期値設定係数                */
	/*      r1 : 縮小比率                     */
	/*      r2 : 拡大比率                     */
	/*      y : 最小値                        */
	/*      x : x(初期値と答え)               */
	/*      return : >=0 : 正常終了(収束回数) */
	/*               =-1 : 収束せず           */
	/******************************************/

	abstract double snx(double x[]);

	int simplex(int max, int n, double k, double eps, double r1, double r2, double y[], double x[])
	{
		double xx[][], yy[], xg[], xr[], xn[], yr, yn, e;
		int i1, i2, fh = -1, fg = -1, fl = -1, count = 0, sw = -1;
						// 初期値の設定
		yy = new double [n+1];
		xg = new double [n];
		xr = new double [n];
		xn = new double [n];
		xx = new double [n+1][n];
		for (i1 = 0; i1 < n+1; i1++) {
			for (i2 = 0; i2 < n; i2++)
				xx[i1][i2] = x[i2];
			if (i1 > 0)
				xx[i1][i1-1] += k;
			yy[i1] = snx(xx[i1]);
		}
						// 最大値,最小値の計算
		for (i1 = 0; i1 < n+1; i1++) {
			if (fh < 0 || yy[i1] > yy[fh])
				fh = i1;
			if (fl < 0 || yy[i1] < yy[fl])
				fl = i1;
		}
		for (i1 = 0; i1 < n+1; i1++) {
			if (i1 != fh && (fg < 0 || yy[i1] > yy[fg]))
				fg = i1;
		}
						// 最小値の計算
		while (count < max && sw < 0) {
			count++;
								// 重心の計算
			for (i1 = 0; i1 < n; i1++)
				xg[i1] = 0.0;
			for (i1 = 0; i1 < n+1; i1++) {
				if (i1 != fh) {
					for (i2 = 0; i2 < n; i2++)
						xg[i2] += xx[i1][i2];
				}
			}
			for (i1 = 0; i1 < n; i1++)
				xg[i1] /= n;
								// 最大点の置き換え
			for (i1 = 0; i1 < n; i1++)
				xr[i1] = 2.0 * xg[i1] - xx[fh][i1];
			yr = snx(xr);
			if (yr >= yy[fh]) {   // 縮小
				for (i1 = 0; i1 < n; i1++)
					xr[i1] = (1.0 - r1) * xx[fh][i1] + r1 * xr[i1];
				yr = snx(xr);
			}
			else if (yr < (yy[fl]+(r2-1.0)*yy[fh])/r2) {   // 拡大
				for (i1 = 0; i1 < n; i1++)
					xn[i1] = r2 * xr[i1] - (r2 - 1.0) * xx[fh][i1];
				yn = snx(xn);
				if (yn <= yr) {
					for (i1 = 0; i1 < n; i1++)
						xr[i1] = xn[i1];
					yr = yn;
				}
			}
			for (i1 = 0; i1 < n; i1++)
				xx[fh][i1] = xr[i1];
			yy[fh] = yr;
								// シンプレックス全体の縮小
			if (yy[fh] >= yy[fg]) {
				for (i1 = 0; i1 < n+1; i1++) {
					if (i1 != fl) {
						for (i2 = 0; i2 < n; i2++)
							xx[i1][i2] = 0.5 * (xx[i1][i2] + xx[fl][i2]);
						yy[i1] = snx(xx[i1]);
					}
				}
			}
								// 最大値,最小値の計算
			fh = -1;
			fg = -1;
			fl = -1;
			for (i1 = 0; i1 < n+1; i1++) {
				if (fh < 0 || yy[i1] > yy[fh])
					fh = i1;
				if (fl < 0 || yy[i1] < yy[fl])
					fl = i1;
			}
			for (i1 = 0; i1 < n+1; i1++) {
				if (i1 != fh && (fg < 0 || yy[i1] > yy[fg]))
					fg = i1;
			}
								// 収束判定
			e = 0.0;
			for (i1 = 0; i1 < n+1; i1++) {
				if (i1 != fl) {
					yr  = yy[i1] - yy[fl];
					e  += yr * yr;
				}
			}
			if (e < eps)
				sw = 0;
		}
	
		if (sw == 0) {
			sw   = count;
			y[0] = yy[fl];
			for (i1 = 0; i1 < n; i1++)
				x[i1] = xx[fl][i1];
		}
	
		return sw;
	}
}