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

データ例,プログラム( species.h,MT.h は除く)

  以下,複数のファイル構成になっています.ファイル間の区切りを「---・・・」で示します.このプログラム例においては,ヘッダファイル MT.h に記述されたメルセンヌ・ツイスタを使用していまが,C++11 で記述可能であれば,標準ライブラリ内に含まれているメルセンヌ・ツイスタ法を使用した乱数生成関数を利用できます.

------------------------ケーススタディデータ------
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 幅 1 平均 0.0 標準偏差 1.0
エリート 2 方法 1 バイアス 0 ステップ 1
最大世代交代数 200

------------------------function.h----------------
/************************/
/* クラスFunctionの定義 */
/************************/
#include "species.h"

class Function : public Species {

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

	public:
					// コンストラクタ
		Function (char *, char *, long);
					// デストラクタ
		~Function ();
					// 全体の実行制御
		void Control();
					// 適応度の計算
		void Adap();
					// 出力
		void Output(int);
};

------------------------Functionのメンバー関数----
/********************************/
/* クラスFunctionのメンバー関数 */
/********************************/
#include "function.h"

/***************************************/
/* コンストラクタ                      */
/*      name1 : Species定義ファイル名  */
/*      name2 : Function定義ファイル名 */
/*      seed : 乱数の初期値            */
/***************************************/
Function::Function (char *name1, char *name2, long seed) : Species (name1, seed)
{
	FILE *in;

	in = fopen(name2, "r");

	fscanf(in, "%*s %d %*s %d", &out_lvl, &out_m);
	fscanf(in, "%*s %s %*s %d", o_file, &out_d);
	fscanf(in, "%*s %d %*s %lf %*s %d %*s %d %*s %d %*s %lf %*s %lf",
           &kosa_m, &kosa, &k_point, &k_vr, &k_method, &k_bias, &k_step);
	fscanf(in, "%*s %d %*s %lf %*s %d %*s %lf %*s %lf",
           &mute_m, &mute, &wd, &m_mean, &m_std);
	fscanf(in, "%*s %d %*s %d %*s %lf %*s %lf",
           &elite, &s_method, &s_bias, &s_step);
	fscanf(in, "%*s %d", &max_gen);

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

	fclose(in);
}

/****************/
/* デストラクタ */
/****************/
Function::~Function()
{
	int i1;

	for (i1 = 0; i1 < size+max_ch; i1++)
		delete [] ind[i1];
	delete [] ind;

	for (i1 = 0; i1 < max_len; i1++)
		delete [] edge[i1];
	delete [] edge;

	delete [] pi;
	delete [] len;
	delete [] kou1;
	delete [] kou2;
	delete [] pi_w;
	delete [] s_w;
	delete [] ro;
}

/**************/
/* 全体の制御 */
/**************/
void Function::Control()
{
	int gen = 1, k1;
					// 初期集団の発生
	Init_std();
					// 評価
	Adap();
					// 出力
	printf("***世代 %d 適応度 max %f (%d) mean %f\n",
           gen, max, max_n, mean);

	if (abs(out_lvl) > 0)
		Output(gen);
					// 世代交代
	for (gen = 2; gen <= max_gen; gen++) {
						// 交叉
		switch (kosa_m) {
			case -1:
				break;
			case 0:
				C_copy();   // 親のコピー
				break;
			case 1:
				C_point(kosa, k_point);   // 多点交叉
				break;
			case 2:
				C_uniform(kosa);   // 一様交叉
				break;
			case 3:
				C_mean(kosa);   // 平均化交叉
				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);
						// 出力
		if (gen%out_d == 0)
			printf("***世代 %d 適応度 max %f (%d) mean %f\n",
                   gen, max, max_n, mean);

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

	gen--;
	k1    = out_m;
	out_m = 0;
	printf("***世代 %d 適応度 max %f (%d) mean %f\n",
            gen, max, max_n, mean);
	Output(gen);
	out_m = k1;
}

/****************/
/* 適応度の計算 */
/****************/
void Function::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 += pow(2.0, y);
				y += 1.0;
			}
			x        *= cv;
			pi[i1]    = sin(3.0*x) + 0.5 * sin(9.0*x) + 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 Function::Output(int gen)
{
	double x, y;
	int i1, i2, k = 0, pr;
	char *now;
	time_t aclock;
	FILE *out;

	if (out_lvl >= 0) {
		printf("   出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ");
		scanf("%d", &pr);
	}
	else
		pr = -1;

	if (pr != 0) {
					// 出力先の決定と評価値の出力
		if (pr > 0) {
			out = stdout;
			getchar();
		}
		else {
			time(&aclock);
			now = ctime(&aclock);
			out = fopen(o_file, "a");
			fprintf(out, "***世代 %d 適応度 max %f (%d) mean %f 時間 %s\n",
                    gen, max, max_n, mean, now);
		}
					// 詳細出力
		for (i1 = 0; i1 < size+max_ch; i1++) {
			if ((pi_w[i1] > 1) && (out_m ==0 || out_m == 1 && i1 == max_n)) {
				fprintf(out, "%d allele", i1);
				for (i2 = 0; i2 < len[i1]; i2++)
					fprintf(out, " %d", ind[i1][i2]);
				x = 0.0;
				y = 0.0;
				for (i2 = len[i1]-1; i2 >= 0; i2--) {
					if (ind[i1][i2] > 0)
						x += pow(2.0, y);
					y += 1.0;
				}
				x *= cv;
				fprintf(out, " x %f y %f\n", x, pi[i1]);
				if (pr > 0) {
					k++;
					if (k == pr) {
						getchar();
						k = 0;
					}
				}
			}
		}

		if (pr < 0)
			fclose(out);
	}
}

------------------------main----------------------
/********************************************************************/
/* f(x) = sin(3.0*x) + 0.5 * sin(9.0*x) + sin(15.0*x + 50) の最大値 */
/*      coded by Y.Suganuma                                         */
/********************************************************************/
#include "function.h"

/****************/
/* main program */
/****************/
int main(int argc, char *argv[])
{
	long *seed;
	int i1, n;
	char **i_file1, **i_file2;
	FILE *in;
	Function *fn;
					// 入力ミス
	if (argc <= 1) {
		printf("***error  ファイル名を入力して下さい\n");
		exit(1);
	}
					// 入力OK
	else {
						// ファイルのオープン
		in = fopen(argv[1], "r");
		if (in == NULL) {
			printf("***error  ファイル名が不適当です\n");
			exit(1);
		}
						// 乱数の初期値と入力データファイル名の入力
		fscanf(in, "%d", &n);   // データの数

		seed    = new long [n];
		i_file1 = new char * [n];
		i_file2 = new char * [n];

		for (i1 = 0; i1 < n; i1++) {
			i_file1[i1] = new char [100];
			i_file2[i1] = new char [100];
			fscanf(in, "%ld %s %s", &seed[i1], i_file1[i1], i_file2[i1]);
		}

		fclose(in);
						// 実行(乱数の初期値を変える)
		for (i1 = 0; i1 < n; i1++) {

			printf("\n+++++ケース %d+++++\n", i1+1);

			fn = new Function (i_file1[i1], i_file2[i1], seed[i1]);

			fn->Control();
		}
	}

	return 0;
}
		

  関数の最大値を求める場合における 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 : 摂動

の中から選択できます.