遺伝的アルゴリズム(関数の最大値)

データ例とプログラム(クラス Species は除く)

  以下,複数のファイル構成になっています.ファイル間の区切りを「---・・・」で示します.

------------------------ケーススタディデータ------
3
12345 data/species.fun data/data.fun
123 data/species.fun data/data.fun
1 data/species.fun data/data.fun

------------------------Species記述データ---------
対立遺伝子上限 1 対立遺伝子下限 0
最大遺伝子長 15 最小遺伝子長(負の時は,最大遺伝子長で固定) -1
遺伝子の重複 1 個体の重複(同じ染色体の個体) 0
集団サイズ 20 子供 20

------------------------Function記述データ--------
出力レベル(負はファイル) 20 出力方法(0:すべて,1:最大) 0
出力ファイル名 result/kekka 表示間隔 1
交叉方法 1 交叉確率 1.0 点 2 位置 0 方法 1 バイアス 0 ステップ 1
突然変異方法 0 突然変異率 0.05 幅 0 平均 0.0 標準偏差 1.0
エリート 2 方法 1 バイアス 0 ステップ 1
最大世代交代数 200

------------------------クラスFunction------------
/************************/
/* クラスFunctionの定義 */
/************************/
import java.io.*;
import java.util.Date;
import java.util.StringTokenizer;

class Function extends Species {

	private double cv;   // 2進数を10進数の変換する係数
	private int max_gen;   // 最大世代交代数
	private int kosa_m;   // 交叉方法
                          //   =-1 : 交叉を使用しない
                          //   =0 : 親のコピー
                          //   =1 : 多点交叉
                          //   =2 : 一様交叉
                          //   =3 : 平均化交叉
	private double kosa;   // 交叉確率
	private int k_point;   // 交差点の数(負の時は,1から-point間のランダム)
	private int k_vr;   // =0 : 両親とも同じ位置で交叉
                        // =1 : 両親が異なる位置で交叉(遺伝子長は可変)
	private int k_method;   // 交叉の時の親の選択方法
                            //   =-1 : ランダム
                            //   =0 : 適応度をそのまま使用
                            //   =1 : 最小値からの差(ただし,α以下の場合はα)
                            //   =2 : 評価値に順位をつけ,減少率βで線形化
	private double k_bias;   // α,または,method=2の場合は初期値
	private double k_step;   // β
	private int mute_m;   // 突然変異方法
                          //   =-1 : 突然変異を使用しない
                          //   =0 : 対立遺伝子への置換
                          //   =1 : 移動
                          //   =2 : 逆位
                          //   =3 : スクランブル
                          //   =4 : 転座
                          //   =5 : 重複
                          //   =6 : 摂動
	private double mute;   // 突然変異率
	private int wd;   // 突然変異に使用する部分遺伝子長
	private double m_mean;   // 摂動の平均値
	private double m_std;   // 摂動の標準偏差
	private int elite;   // エリート選択で残す数
	private int s_method;   // ルーレット板の作成方法
                            //   =0 : 適応度をそのまま使用
                            //   =1 : 最小値からの差(ただし,α以下の場合はα)
                            //   =2 : 評価値に順位をつけ,減少率βで線形化
	private double s_bias;   // α,または,s_method=2の場合は初期値
	private double s_step;   // β
	private int out_d;   // 表示間隔
	private int out_lvl;   // 出力レベル
                           //   =0 : 最終出力だけ
                           //   n>0 : n世代毎に出力(負の時はファイル)
	private int out_m;   // 出力方法
                         //   =0 : すべてを出力
                         //   =1 : 最大適応度の個体だけを出力
	private String o_file;   // 出力ファイル名

	/***************************************/
	/* コンストラクタ                      */
	/*      name1 : Species定義ファイル名  */
	/*      name2 : Function定義ファイル名 */
	/*      seed : 乱数の初期値            */
	/***************************************/
	Function (String name1, String name2, int seed) throws IOException, FileNotFoundException
	{
		super (name1, seed);

		String line;
		StringTokenizer dt;
		BufferedReader in = new BufferedReader(new FileReader(name2));

		line = in.readLine();
		dt   = new StringTokenizer(line, " ");
		dt.nextToken();
		out_lvl = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		out_m = Integer.parseInt(dt.nextToken());

		line = in.readLine();
		dt   = new StringTokenizer(line, " ");
		dt.nextToken();
		o_file = dt.nextToken();
		dt.nextToken();
		out_d = Integer.parseInt(dt.nextToken());

		line = in.readLine();
		dt   = new StringTokenizer(line, " ");
		dt.nextToken();
		kosa_m = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		kosa = Double.parseDouble(dt.nextToken());
		dt.nextToken();
		k_point = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		k_vr = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		k_method = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		k_bias = Double.parseDouble(dt.nextToken());
		dt.nextToken();
		k_step = Double.parseDouble(dt.nextToken());

		line = in.readLine();
		dt   = new StringTokenizer(line, " ");
		dt.nextToken();
		mute_m = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		mute = Double.parseDouble(dt.nextToken());
		dt.nextToken();
		wd = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		m_mean = Double.parseDouble(dt.nextToken());
		dt.nextToken();
		m_std = Double.parseDouble(dt.nextToken());

		line = in.readLine();
		dt   = new StringTokenizer(line, " ");
		dt.nextToken();
		elite = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		s_method = Integer.parseInt(dt.nextToken());
		dt.nextToken();
		s_bias = Double.parseDouble(dt.nextToken());
		dt.nextToken();
		s_step = Double.parseDouble(dt.nextToken());

		line = in.readLine();
		dt   = new StringTokenizer(line, " ");
		dt.nextToken();
		max_gen = Integer.parseInt(dt.nextToken());

		cv = 1.0 / (Math.pow(2.0, (double)max_len) - 1.0);

		in.close();
	}

	/**************/
	/* 全体の制御 */
	/**************/
	void Control() throws IOException, FileNotFoundException
	{
		int gen = 1, k1;
						// 初期集団の発生
		Init_std();
						// 評価
		Adap();
						// 出力
		System.out.println("***世代 " + gen + " 適応度 max " + max +
                           " (" + max_n + ") mean " + mean);

		if (Math.abs(out_lvl) > 0)
			Output(gen);
						// 世代交代
		for (gen = 2; gen <= max_gen; gen++) {
							// 交叉
			switch (kosa_m) {
				case -1:
					break;
				case 0:
					C_copy(2, max_ch/2, k_method, k_bias, k_step);   // 親のコピー
					break;
				case 1:
					C_point(kosa, k_point, k_vr, k_method, k_bias, k_step);   // 多点交叉
					break;
				case 2:
					C_uniform(kosa, k_method, k_bias, k_step);   // 一様交叉
					break;
				case 3:
					C_mean(kosa, k_method, k_bias, k_step);   // 平均化交叉
					break;
				default:
					break;
			}
							// 突然変異
			switch (mute_m) {
				case -1:
					break;
				case 0:
					M_alle(mute);   // 対立遺伝子への置換
					break;
				case 1:
					M_move(mute);   // 移動
					break;
				case 2:
					M_inv(mute, wd);   // 逆位
					break;
				case 3:
					M_scram(mute, wd);   // スクランブル
					break;
				case 4:
					M_chg(mute, wd);   // 転座
					break;
				case 5:
					M_dup(mute, wd);   // 重複
					break;
				case 6:
					M_per(mute, wd, m_mean, m_std);   // 摂動
					break;
				default:
					break;
			}
							// 適応度
			Adap();
							// 淘汰
			S_roul(elite, s_method, s_bias, s_step);
							// 出力
			if (gen%out_d == 0)
				System.out.println("***世代 " + gen + " 適応度 max " + max +
                                   " (" + max_n + ") mean " + mean);

			if (Math.abs(out_lvl) > 0) {
				if (gen%Math.abs(out_lvl) == 0)
					Output(gen);
			}
		}

		gen--;
		k1    = out_m;
		out_m = 0;
		System.out.println("***世代 " + gen + " 適応度 max " + max +
                           " (" + max_n + ") mean " + mean);
		Output(gen);
		out_m = k1;
	}

	/****************/
	/* 適応度の計算 */
	/****************/
	void Adap()
	{
		double x, y;
		int i1, i2, n = 0;

		max   = 0.0;
		max_n = -1;
		mean  = 0.0;

		for (i1 = 0; i1 < size+max_ch; i1++) {

			if (pi_w[i1] == 1) {
				x = 0.0;
				y = 0.0;
				for (i2 = len[i1]-1; i2 >= 0; i2--) {
					if (ind[i1][i2] > 0)
						x += Math.pow(2.0, y);
					y += 1.0;
				}
				x        *= cv;
				pi[i1]    = Math.sin(3.0*x) + 0.5 * Math.sin(9.0*x) +
                            Math.sin(15.0*x+50.0);
				pi_w[i1]  = 2;
			}

			if (pi_w[i1] > 0) {
				mean += pi[i1];
				n++;
				if (max_n < 0 || pi[i1] > max) {
					max   = pi[i1];
					max_n = i1;
				}
			}
		}

		mean /= n;
	}

	/*****************************/
	/* 結果の出力                */
	/*      gen : 現在の世代番号 */
	/*****************************/
	void Output(int gen) throws IOException, FileNotFoundException
	{
		double x, y;
		int i1, i2, k = 0, pr;
		String now;
		PrintStream out = null;
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		if (out_lvl >= 0) {
			System.out.print("   出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ");
			pr = Integer.parseInt(in.readLine());
		}
		else
			pr = -1;

		if (pr != 0) {
						// 出力先の決定と評価値の出力
			if (pr > 0)
				out = System.out;
			else {
				Date newtime = new Date();   // 現在時刻の獲得
				now = newtime.toString();    // 文字列への変換
				out = new PrintStream(new FileOutputStream(o_file, true));
				out.println("***世代 " + gen + " 適応度 max " + max +
                            " (" + max_n + ") mean " + mean + " 時間 " + now);
			}
						// 詳細出力
			for (i1 = 0; i1 < size+max_ch; i1++) {
				if ((pi_w[i1] > 1) && (out_m ==0 || out_m == 1 && i1 == max_n)) {
					out.print(i1 + " allele");
					for (i2 = 0; i2 < len[i1]; i2++)
						out.print(" " + ind[i1][i2]);
					x = 0.0;
					y = 0.0;
					for (i2 = len[i1]-1; i2 >= 0; i2--) {
						if (ind[i1][i2] > 0)
							x += Math.pow(2.0, y);
						y += 1.0;
					}
					x *= cv;
					out.println(" x " + x + " y " + pi[i1]);
					if (pr > 0) {
						k++;
						if (k == pr) {
							in.readLine();
							k = 0;
						}
					}
				}
			}

			if (pr < 0)
				out.close();
		}
	}
}

------------------------クラスOpt(main)-----------
/********************************************************************/
/* f(x) = sin(3.0*x) + 0.5 * sin(9.0*x) + sin(15.0*x + 50) の最大値 */
/*      coded by Y.Suganuma                                         */
/********************************************************************/
import java.io.*;
import java.util.StringTokenizer;

public class Opt {

	/****************/
	/* main program */
	/****************/
	public static void main(String args[]) throws IOException, FileNotFoundException
	{
		int i1, n, seed[];
		String i_file1[], i_file2[], line;
		Function fn;
		StringTokenizer dt;
		BufferedReader in = new BufferedReader(new FileReader(args[0]));
						// 入力ミス
		if (args.length == 0) {
			System.out.print("***error  ファイル名を入力して下さい\n");
			System.exit(1);
		}
						// 入力OK
		else {
							// 乱数の初期値と入力データファイル名の入力
			n      = Integer.parseInt(in.readLine());

			seed    = new int [n];
			i_file1 = new String [n];
			i_file2 = new String [n];

			for (i1 = 0; i1 < n; i1++) {
				line = in.readLine();
				dt   = new StringTokenizer(line, " ");
				seed[i1] = Integer.parseInt(dt.nextToken());
				i_file1[i1] = dt.nextToken();
				i_file2[i1] = dt.nextToken();
			}

			in.close();
							// 実行(乱数の初期値を変える)
			for (i1 = 0; i1 < n; i1++) {
				System.out.print("\n+++++ケース " + (i1+1) + "+++++\n");
								// 入力と初期設定
				fn = new Function (i_file1[i1], i_file2[i1], seed[i1]);
								// 最適化
				fn.Control();
			}
		}
	}
}
		

  関数の最大値を求める場合における Species 記述ファイルは,たとえば,以下のようになります.
対立遺伝子上限 1 対立遺伝子下限 0
最大遺伝子長 15 最小遺伝子長(負の時は,最大遺伝子長で固定) -1
遺伝子の重複 1 個体の重複(同じ染色体の個体) 0
集団サイズ 20 子供 20		
  データの意味は TSP の場合とほぼ同様ですので省略します.TSP の場合と同様,入力データによっては,そのケースには不必要なデータが現れることがあります.その場合においても,データの説明とデータは削除せず残しておいてください(内部的には,使用されません).x の値が染色体に対応し,15 ビットのビット列で表現している点に注意してください.クラス TSP の代わりに,関数の最適値を求めるためのクラス Function を利用していますが,その記述ファイルは,たとえば,以下のようになります.
出力レベル(負はファイル) 20 出力方法(0:すべて,1:最大) 0
出力ファイル名 result/kekka 表示間隔 1
交叉方法 1 交叉確率 1.0 点 2 位置 0 方法 1 バイアス 0 ステップ 1
突然変異方法 0 突然変異率 0.05 幅 1 平均 0.0 標準偏差 1.0
エリート 2 方法 1 バイアス 0 ステップ 1
最大世代交代数 200		
  データの意味については,TSP の場合とほぼ同様ですが,交叉方法および突然変異方法に対して選択できる方法が異なってきます.交叉方法に対しては,

=-1 : 交叉を使用しない
=0 : 親のコピー
=1 : 多点交叉
=2 : 一様交叉
=3 : 平均化交叉

の中から選択でき,また,突然変異に方法に対しては,

=-1 : 突然変異を使用しない
=0 : 対立遺伝子への置換
=1 : 移動
=2 : 逆位
=3 : スクランブル
=4 : 転座
=5 : 重複
=6 : 摂動

の中から選択できます.