<?php
/***********************************/
/* 遺伝的アルゴリズムによるTSPの解 */
/* coded by Y.Suganuma */
/***********************************/
/***********************/
/* クラスSpeciesの定義 */
/***********************/
class Species {
protected $max; // 最大適応度
protected $mean; // 平均適応度
protected $pi; // 適応度
protected $ro; // ルーレット板
protected $allele_u; // 対立遺伝子上限
protected $allele_l; // 対立遺伝子下限
protected $size; // 個体総数
protected $max_ch; // 子供の数の最大値
protected $max_len; // 最大遺伝子長
protected $min_len; // 最小遺伝子長(負の時は,最大遺伝子長で固定)
protected $max_n; // 最大適応度の個体番号
protected $dup_a; // 遺伝子の重複
// =0 : 重複を許さない
// =1 : 重複を許す
protected $dup_s; // 個体の重複(同じ染色体の個体)
// =0 : 重複を許さない
// =1 : 重複を許す
protected $ind; // 集団(個体の集まり)
protected $len; // 各個体の遺伝子長
protected $kou1; // 交叉・突然変異用作業場所1
protected $kou2; // 交叉・突然変異用作業場所2
protected $s_w; // 淘汰用指標(選択された回数)
protected $edge; // エッジ組み替え交叉用ワークエリア
protected $pi_w; // 適応度計算指標
// =0 : 未使用
// =1 : 適応度計算前(突然変異はこの個体だけに適用)
// =2 : 適応度計算済み(交叉時に親とみなす)
/****************************/
/* コンストラクタ */
/* name : ファイル名 */
/* seed : 乱数の初期値 */
/****************************/
function Species($name, $seed)
{
/*
データの入力
*/
$in = fopen($name, "rb");
fscanf($in, "%*s %d %*s %d", $this->allele_u, $this->allele_l);
fscanf($in, "%*s %d %*s %d", $this->max_len, $this->min_len);
fscanf($in, "%*s %d %*s %d", $this->dup_a, $this->dup_s);
fscanf($in, "%*s %d %*s %d", $this->size, $this->max_ch);
/*
データのチェック
*/
if ($this->size <= 0)
exit("***error 個体総数≦0 (Constructor)\n");
if ($this->max_ch < 0)
exit("***error 子供の数<0 (Constructor)\n");
if ($this->max_len <= 0 || $this->min_len == 0)
exit("***error 遺伝子長≦0 (Constructor)\n");
if ($this->max_len < $this->min_len)
exit("***error 最大遺伝子長<最小遺伝子長 (Constructor)\n");
if ($this->allele_u <= $this->allele_l)
exit("***error 対立遺伝子上限≦対立遺伝子下限 (Constructor)\n");
$kind = $this->allele_u - $this->allele_l + 1;
if ($this->dup_a == 0 && $this->max_len > $kind)
exit("***error 遺伝子の重複を防ぐことはできない (Constructor)\n");
/*
領域の確保
*/
$num = $this->size + $this->max_ch;
$this->ind = array($num);
for ($i1 = 0; $i1 < $num; $i1++)
$this->ind[$i1] = array($this->max_len);
$this->edge = array($this->max_len);
for ($i1 = 0; $i1 < $this->max_len; $i1++)
$this->edge[$i1] = array(5);
$this->pi = array($num);
$this->ro = array($num);
$this->len = array($num);
$this->kou1 = array($this->max_len);
$this->kou2 = array($this->max_len);
$this->s_w = array($num);
$this->pi_w = array($num);
/*
乱数の初期設定
*/
mt_srand($seed);
}
/**************************************************/
/* 場所を探す */
/* n : >=0 : n番目の親を捜す */
/* -1 : 空いている場所を探す */
/* return : 親の場所,または,空いている場所 */
/* (存在しないときは負の値) */
/**************************************************/
function Position($n)
{
$k = -1;
$sw = 0;
/*
空いている場所を探す
*/
if ($n < 0) {
for ($i1 = 0; $i1 < $this->size+$this->max_ch && $k < 0; $i1++) {
if ($this->pi_w[$i1] == 0)
$k = $i1;
}
if ($k < 0)
exit("***error 空いている場所がない --Position--\n");
}
/*
n番目の親($this->pi_w[i]=2)を捜す
*/
else {
for ($i1 = 0; $i1 < $this->size+$this->max_ch && $sw == 0; $i1++) {
if ($this->pi_w[$i1] == 2) {
$k++;
if ($k == $n) {
$sw = 1;
$k = $i1;
}
}
}
}
return $k;
}
/*******************************************************************/
/* 個体の選択 */
/* method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* bias : α,または,method=2の場合は初期値(default=0) */
/* step : β(default=1) */
/* return : 個体番号 */
/*******************************************************************/
function Select($method = -1, $bias = 0.0, $step = 1.0)
{
$sum = 0.0;
// ルーレット板の用意
switch ($method) {
// ランダム
case -1:
$n = 0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$n++;
}
$sum = 1.0 / $n;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$this->ro[$i1] = $sum;
}
break;
// 評価値をそのまま利用
case 0:
$n = 0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1) {
$sum += $this->pi[$i1];
$n++;
}
}
if (abs($sum) > 1.0e-10) {
$sum = 1.0 / abs($sum);
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$this->ro[$i1] = $this->pi[$i1] * $sum;
}
}
else {
$sum = 1.0 / $n;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$this->ro[$i1] = $sum;
}
}
break;
// 最小値からの差
case 1:
$min = -1;
$n = 0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1) {
$n++;
if ($min < 0 || $this->pi[$i1] < $this->pi[$min])
$min = $i1;
}
}
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1) {
$this->ro[$i1] = $this->pi[$i1] - $this->pi[$min];
if ($this->ro[$i1] < $bias)
$this->ro[$i1] = $bias;
$sum += $this->ro[$i1];
}
}
if ($sum > 1.0e-10) {
$sum = 1.0 / $sum;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$this->ro[$i1] *= $sum;
}
}
else {
$sum = 1.0 / $n;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$this->ro[$i1] = $sum;
}
}
break;
// 線形化
case 2:
$n = 0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1) {
$this->ro[$i1] = -1.0;
$n++;
}
else
$this->ro[$i1] = 1.0;
}
$sw = 0;
$sum = $bias;
while ($sw == 0) {
$min = -1;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->ro[$i1] < 0.0 && ($min < 0 || $this->pi[$i1] < $this->pi[$min]))
$min = $i1;
}
if ($min < 0)
$sw = 1;
else {
$this->ro[$min] = $sum;
$sum += $step;
}
}
$sum = 1.0 / (0.5 * (2.0 * $bias + $step * ($n - 1)) * $n);
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$this->ro[$i1] *= $sum;
}
break;
}
$sum = 0.0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1) {
$sum += $this->ro[$i1];
$this->ro[$i1] = $sum;
}
}
// 選択
$x = uniform();
$sw = 0;
$k = 0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch && $sw == 0; $i1++) {
if ($this->pi_w[$i1] > 1) {
if ($x <= $this->ro[$i1]) {
$sw = 1;
$k = $i1;
}
}
}
return $k;
}
/********************/
/* 標準的な初期設定 */
/********************/
function Init_std()
{
/*
初期設定
*/
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($i1 < $this->size)
$this->pi_w[$i1] = 1; // 適応度の計算前
else
$this->pi_w[$i1] = 0; // 未使用
}
/*
遺伝子の決定
*/
for ($i1 = 0; $i1 < $this->size; $i1++) {
$sw1 = 0;
while ($sw1 == 0) {
// 遺伝子長の決定
if ($this->min_len < 0)
$length = $this->max_len;
else {
$length = intval(uniform() * ($this->max_len - $this->min_len + 1) + $this->min_len);
if ($length > $this->max_len)
$length = $this->max_len;
}
$this->len[$i1] = $length;
// 遺伝子の決定
for ($i2 = 0; $i2 < $length; $i2++) {
$sw2 = 0;
while ($sw2 == 0) {
$lid = intval(uniform() * ($this->allele_u - $this->allele_l + 1) + $this->allele_l);
if ($lid > $this->allele_u)
$lid = $this->allele_u;
$this->ind[$i1][$i2] = $lid;
// 重複遺伝子のチェック
$sw2 = 1;
if ($this->dup_a == 0) {
for ($i3 = 0; $i3 < $i2 && $sw2 > 0; $i3++) {
if ($lid == $this->ind[$i1][$i3])
$sw2 = 0;
}
}
}
}
// 重複個体のチェック
$sw1 = 1;
if ($this->dup_s == 0) {
for ($i2 = 0; $i2 < $i1 && $sw1 > 0; $i2++) {
if ($this->len[$i1] == $this->len[$i2]) {
$sw2 = 0;
for ($i3 = 0; $i3 < $this->len[$i1] && $sw2 == 0; $i3++) {
if ($this->ind[$i1][$i3] != $this->ind[$i2][$i3])
$sw2 = 1;
}
if ($sw2 == 0)
$sw1 = 0;
}
}
}
}
}
}
/****************************************************/
/* 標準的な出力 */
/* sw : 出力レベル */
/* =0 : 最終出力だけ */
/* n>0 : n世代毎に出力(負はファイル) */
/* out_m : 出力方法 */
/* =0 : すべての個体を出力 */
/* =1 : 最大適応度の個体だけを出力 */
/* gen : 現在の世代番号 */
/* name : 出力ファイル名 */
/****************************************************/
function Out_std($sw, $out_m, $gen, $name)
{
$k = 0;
if ($sw >= 0) {
printf(" 出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ");
fscanf(STDIN, "%d", $pr);
}
else
$pr = -1;
if ($pr != 0) {
// 出力先の決定と評価値の出力
if ($pr > 0) {
$out = STDOUT;
fgets(STDIN);
}
else {
$x = getdate();
$now = $x["hours"]."時".$x["minutes"]."分".$x["seconds"]."秒";
$out = fopen($name, "ab");
fwrite($out, "***世代 ".$gen." 適応度 max ".$this->max." (".$this->max_n.") mean ".$this->mean." 時間 ".$now."\n");
}
// 詳細出力
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if (($this->pi_w[$i1] > 1) && ($out_m == 0 || $out_m == 1 && $i1 == $this->max_n)) {
$str = $i1." allele";
for ($i2 = 0; $i2 < $this->len[$i1]; $i2++)
$str += " ".$this->ind[$i1][$i2];
$str += " value ".$this->pi[$i1];
fwrite($out, $str."\n");
if ($pr > 0) {
$k++;
if ($k == $pr) {
fgets(STDIN);
$k = 0;
}
}
}
}
if ($pr < 0)
fclose($out);
}
}
/*******************************************************************/
/* 交叉(親のコピー) */
/* method : =2 : 有性(2つの親から2つの子供)(default) */
/* =1 : 1つの親から1つの子供 */
/* pair : method=2 の時は親のペア数(default=max_ch/2) */
/* method=1 の時は親の数(=子供の数) */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_copy($method = 2, $pair = 0, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータチェック
*/
if ($method != 1)
$method = 2;
if ($pair <= 0)
$pair = ($method==2) ? intval($this->max_ch/2) : $this->max_ch;
else {
if ($method == 2 && 2*$pair > $this->max_ch || $method == 1 && $pair > $this->max_ch)
exit("***error 子供が多すぎる (C_copy)\n");
}
/*
実行
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// コピー
for ($i2 = 0; $i2 < $method; $i2++) {
$p = ($i2 == 0) ? $p1 : $p2;
$k = $this->Position(-1);
$this->len[$k] = $this->len[$p];
$this->pi_w[$k] = 1;
for ($i3 = 0; $i3 < $this->len[$k]; $i3++)
$this->ind[$k][$i3] = $this->ind[$p][$i3];
}
}
}
/*******************************************************************/
/* 交叉(多点交叉) */
/* kosa : 交叉確率 */
/* k_point : 交叉点の数(default=1) */
/* (負の時は,1から-k_point間のランダム) */
/* k_vr : =0 : 両親とも同じ位置で交叉(default) */
/* =1 : 両親が異なる位置で交叉(遺伝子長は可変) */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_point($kosa, $k_point = 1, $k_vr = 0, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
$mn = 0;
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a == 0)
exit("***error 交叉方法が不適当 (C_point)\n");
$abs_p = abs($k_point);
if ($abs_p == 0 || $abs_p > $this->max_len-1 || $this->min_len > 0 && $abs_p > $this->min_len-1)
exit("***error 交叉点の数が不適当 (C_point)\n");
if ($k_vr > 0 && $this->min_len < 0)
exit("***error 遺伝子長は可変でなければならない (C_point)\n");
/*
交叉
*/
$num = $k_point;
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 交叉位置の数の決定
if ($k_point < 0) {
$num = intval(uniform() * $abs_p + 1);
if ($num > $abs_p)
$num = $abs_p;
}
// 交叉位置の決定(点の後ろで交叉)
for ($i2 = 0; $i2 < $num; $i2++) {
// 親1の交叉位置
$sw = 0;
while ($sw == 0) {
$sw = 1;
$this->kou1[$i2] = intval(uniform() * ($this->len[$p1] - 1));
if ($this->kou1[$i2] > $this->len[$p1]-2)
$this->kou1[$i2] = $this->len[$p1] - 2;
if ($k_vr == 0 && $this->kou1[$i2] > $this->len[$p2]-2)
$this->kou1[$i2] = $this->len[$p2] - 2;
for ($i3 = 0; $i3 < $i2 && $sw > 0; $i3++) {
if ($this->kou1[$i3] == $this->kou1[$i2])
$sw = 0;
}
}
// 親2の交叉位置
if ($k_vr > 0) {
$sw = 0;
while ($sw == 0) {
$sw = 1;
$this->kou2[$i2] = intval(uniform() * ($this->len[$p2] - 1));
if ($this->kou2[$i2] > $this->len[$p2]-2)
$this->kou2[$i2] = $this->len[$p2] - 2;
for ($i3 = 0; $i3 < $i2 && $sw > 0; $i3++) {
if ($this->kou2[$i3] == $this->kou2[$i2])
$sw = 0;
}
}
}
}
// 交叉の実行
// 親1のt11からt12を子1のc1へコピー
// 親2のt21からt22を子2のc2へコピー
// 次は,
// 親1のt11からt12を子2のc2へコピー
// 親2のt21からt22を子1のc1へコピー
// ・・・・・
$c1 = 0;
$c2 = 0;
$t11 = 0;
$t21 = 0;
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
for ($i2 = 0; $i2 < $num+1; $i2++ ) {
// 次の交叉位置を求める
if ($i2 == $num) { // 最後
$t12 = $this->len[$p1];
$t22 = $this->len[$p2];
}
else {
// 親1
$t12 = $this->max_len;
for ($i3 = 0; $i3 < $num; $i3++) {
if ($this->kou1[$i3] >= 0 && $this->kou1[$i3] <= $t12) {
$t12 = $this->kou1[$i3];
$mn = $i3;
}
}
$this->kou1[$mn] = -1;
$t12++;
// 親2
if ($k_vr == 0)
$t22 = $t12;
else {
$t22 = $this->max_len;
for ($i3 = 0; $i3 < $num; $i3++) {
if ($this->kou2[$i3] >= 0 && $this->kou2[$i3] <= $t22) {
$t22 = $this->kou2[$i3];
$mn = $i3;
}
}
$this->kou2[$mn] = -1;
$t22++;
}
}
// 指定箇所のコピー
for ($i3 = $t11; $i3 < $t12; $i3++) {
if ($i2%2 == 0) {
if ($c1 < $this->max_len) {
$this->ind[$k1][$c1] = $this->ind[$p1][$i3];
$c1++;
}
}
else {
if ($c2 < $this->max_len) {
$this->ind[$k2][$c2] = $this->ind[$p1][$i3];
$c2++;
}
}
}
for ($i3 = $t21; $i3 < $t22; $i3++) {
if ($i2%2 == 0) {
if ($c2 < $this->max_len) {
$this->ind[$k2][$c2] = $this->ind[$p2][$i3];
$c2++;
}
}
else {
if ($c1 < $this->max_len) {
$this->ind[$k1][$c1] = $this->ind[$p2][$i3];
$c1++;
}
}
}
// 交叉位置の移動
$t11 = $t12;
$t21 = $t22;
}
}
}
}
/*******************************************************************/
/* 交叉(一様交叉.[0,1]を等確率で発生させ,1であれば, */
/* 親1,0であれば親2の遺伝子を子1が受け継ぐ) */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_uniform($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a == 0)
exit("***error 交叉方法が不適当 (C_uniform)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_uniform)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
// 交叉
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
if (uniform() > 0.5) {
$this->ind[$k1][$i2] = $this->ind[$p1][$i2];
$this->ind[$k2][$i2] = $this->ind[$p2][$i2];
}
else {
$this->ind[$k1][$i2] = $this->ind[$p2][$i2];
$this->ind[$k2][$i2] = $this->ind[$p1][$i2];
}
}
}
}
}
/*******************************************************************/
/* 交叉(平均化交叉.2つの親の平均値を受け継ぐ) */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_mean($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_mean)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $this->max_ch; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(1, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k = $this->Position(-1);
$this->len[$k] = $this->len[$p1];
$this->pi_w[$k] = 1;
// 交叉
for ($i2 = 0; $i2 < $this->len[$k]; $i2++)
$this->ind[$k][$i2] = ($this->ind[$p1][$i2] + $this->ind[$p2][$i2]) / 2;
}
}
}
/*******************************************************************/
/* 交叉(循環交叉.ランダムに1点を選択し,その位置にある遺伝子を */
/* そのまま各子供が選択する.その位置にある親2(1)の遺伝 */
/* 子を,その遺伝子の親1(2)の場所に,子1(2)が受け継 */
/* ぐ(ただし,doubleの場合は,この手続きをのぞく).この手 */
/* 続きを,すでに受け継いだ遺伝子の位置が選択されるまで繰り */
/* 返し,残りの遺伝子については,子1(2)は,親2(1)の */
/* 遺伝子をその順番通りに受け継ぐ) */
/* 2 4 1 3 6 5 + + 1 + + 5 3 2 1 4 6 5 */
/* * → → */
/* 3 2 5 4 1 6 + + 5 + 1 + 2 4 5 3 1 6 */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_cycle($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a != 0)
exit("***error 交叉方法が不適当 (C_cycle)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_cycle)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 初期設定
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
$this->kou1[$i2] = 0;
$this->kou2[$i2] = 0;
}
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
// 交叉
$sw = 0;
while ($sw == 0) {
$sw = 1;
$p = intval(uniform() * $this->len[$p1]);
if ($p >= $this->len[$p1])
$p = $this->len[$p1] - 1;
if ($this->kou1[$p] == 0 && $this->kou2[$p] == 0) {
$this->kou1[$p] = 1;
$this->kou2[$p] = 1;
$this->ind[$k1][$p] = $this->ind[$p1][$p];
$this->ind[$k2][$p] = $this->ind[$p2][$p];
for ($i2 = 0; $i2 < $this->len[$p1] && $sw > 0; $i2++) {
if ($this->ind[$p2][$p] == $this->ind[$p1][$i2]) {
$this->ind[$k1][$i2] = $this->ind[$p1][$i2];
$this->kou1[$i2] = 1;
$sw = 0;
}
}
$sw = 1;
for ($i2 = 0; $i2 < $this->len[$p2] && $sw > 0; $i2++) {
if ($this->ind[$p1][$p] == $this->ind[$p2][$i2]) {
$this->ind[$k2][$i2] = $this->ind[$p2][$i2];
$this->kou2[$i2] = 1;
$sw = 0;
}
}
}
}
$sw = 0;
$i2 = 0;
$i3 = 0;
while ($sw == 0) {
while ($sw == 0 && $i2 < $this->len[$p1]) {
if ($this->kou1[$i2] == 0)
$sw = 1;
else
$i2++;
}
$sw = 0;
while ($sw == 0 && $i3 < $this->len[$p2]) {
if ($this->kou2[$i3] == 0)
$sw = 1;
else
$i3++;
}
if ($i2 < $this->len[$p1] && $i3 < $this->len[$p2]) {
$this->ind[$k1][$i2] = $this->ind[$p2][$i3];
$this->ind[$k2][$i3] = $this->ind[$p1][$i2];
$sw = 0;
$i2++;
$i3++;
}
else
$sw = 1;
}
}
}
}
/*******************************************************************/
/* 交叉(部分的交叉.ランダムに1点を選択し,その位置にある親1と */
/* 親2の遺伝子を取り出す.次に,親1と親2の染色体上で,こ */
/* の2つの遺伝子の位置を交換する.この操作を,選択した点よ */
/* り右にあるすべての遺伝子に対して実施する */
/* 2 4 1 3 6 5 2 4 5 3 6 1 */
/* * → → ・・・・・ */
/* 3 2 5 4 1 6 3 2 1 4 5 6 */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_part($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a != 0)
exit("***error 交叉方法が不適当 (C_part)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_part)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
// 交叉
$p = intval(uniform() * $this->len[$p1]);
if ($p >= $this->len[$p1])
$p = $this->len[$p1] - 1;
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
$this->ind[$k1][$i2] = $this->ind[$p1][$i2];
$this->ind[$k2][$i2] = $this->ind[$p2][$i2];
}
for ($i2 = $p; $i2 < $this->len[$p1]; $i2++) {
$sw = 0;
$lv = $this->ind[$k1][$i2];
for ($i3 = 0; $i3 < $this->len[$p1] && $sw == 0; $i3++) {
if ($this->ind[$k2][$i2] == $this->ind[$k1][$i3]) {
$this->ind[$k1][$i2] = $this->ind[$k1][$i3];
$this->ind[$k1][$i3] = $lv;
$sw = 1;
}
}
$sw = 0;
for ($i3 = 0; $i3 < $this->len[$p1] && $sw == 0; $i3++) {
if ($lv == $this->ind[$k2][$i3]) {
$this->ind[$k2][$i3] = $this->ind[$k2][$i2];
$this->ind[$k2][$i2] = $lv;
$sw = 1;
}
}
}
}
}
}
/*******************************************************************/
/* 交叉(順序交叉.ランダムに切れ目を決定し,子1に対し,切れ目の */
/* 左側では,親1の遺伝子をそのまま受け継ぎ,右側では,親1 */
/* の遺伝子を親2の遺伝子の出現順序に並べ替える. */
/* 2 4 1 3 6 5 2 4 1 3 5 6 */
/* * → */
/* 3 2 5 4 1 6 3 2 5 4 1 6 */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_seq($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a != 0)
exit("***error 交叉方法が不適当 (C_seq)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_seq)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
// 交叉
$p = intval(uniform() * ($this->len[$p1] - 1));
if ($p >= $this->len[$p1]-1)
$p = $this->len[$p1] - 2;
for ($i2 = 0; $i2 <= $p; $i2++) {
$this->ind[$k1][$i2] = $this->ind[$p1][$i2];
$this->ind[$k2][$i2] = $this->ind[$p2][$i2];
}
$pp = 0;
for ($i2 = $p+1; $i2 < $this->len[$p1]; $i2++) {
$sw = 0;
for ($i3 = $pp; $i3 < $this->len[$p2] && $sw == 0; $i3++) {
for ($i4 = $p+1; $i4 < $this->len[$p1] && $sw == 0; $i4++) {
if ($this->ind[$p2][$i3] == $this->ind[$p1][$i4]) {
$sw = 1;
$pp = $i3 + 1;
$this->ind[$k1][$i2] = $this->ind[$p1][$i4];
}
}
}
}
$pp = 0;
for ($i2 = $p+1; $i2 < $this->len[$p2]; $i2++) {
$sw = 0;
for ($i3 = $pp; $i3 < $this->len[$p1] && $sw == 0; $i3++) {
for ($i4 = $p+1; $i4 < $this->len[$p2] && $sw == 0; $i4++) {
if ($this->ind[$p1][$i3] == $this->ind[$p2][$i4]) {
$sw = 1;
$pp = $i3 + 1;
$this->ind[$k2][$i2] = $this->ind[$p2][$i4];
}
}
}
}
}
}
}
/*******************************************************************/
/* 交叉(一様順序交叉.位置の集合をランダムに選択し,一方の親の選 */
/* 択された位置における遺伝子の順序に従って,他の親の遺伝子 */
/* を並べ替える */
/* 2 4 1 3 6 5 2 4 1 3 6 5 */
/* * * → */
/* 3 2 5 4 1 6 4 2 5 3 1 6 */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_useq($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a != 0)
exit("***error 交叉方法が不適当 (C_useq)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_useq)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
// 交叉
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
$this->ind[$k1][$i2] = $this->ind[$p1][$i2];
$this->ind[$k2][$i2] = $this->ind[$p2][$i2];
$this->kou1[$i2] = (uniform() < 0.5) ? 0 : 1;
}
$p = 0;
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
if ($this->kou1[$i2] > 0) {
$sw = 0;
for ($i3 = $p; $i3 < $this->len[$p2] && $sw == 0; $i3++) {
for ($i4 = 0; $i4 < $this->len[$p1] && $sw == 0; $i4++) {
if ($this->ind[$p2][$i3] == $this->ind[$p1][$i4] && $this->kou1[$i4] > 0) {
$sw = 1;
$p = $i3 + 1;
$this->ind[$k1][$i2] = $this->ind[$p1][$i4];
}
}
}
}
}
$p = 0;
for ($i2 = 0; $i2 < $this->len[$p2]; $i2++) {
if ($this->kou1[$i2] > 0) {
$sw = 0;
for ($i3 = $p; $i3 < $this->len[$p1] && $sw == 0; $i3++) {
for ($i4 = 0; $i4 < $this->len[$p2] && $sw == 0; $i4++) {
if ($this->ind[$p1][$i3] == $this->ind[$p2][$i4] && $this->kou1[$i4] > 0) {
$sw = 1;
$p = $i3 + 1;
$this->ind[$k2][$i2] = $this->ind[$p2][$i4];
}
}
}
}
}
}
}
}
/*******************************************************************/
/* 交叉(一様位置交叉.位置の集合をランダムに選択し,一方の親の選 */
/* 択された位置における遺伝子の位置に,他の親の同じ遺伝子を */
/* 配置する.残りの遺伝子は,親と同じ順序に配置する. */
/* 2 4 1 3 6 5 + + 5 + 1 + 2 4 5 3 1 6 */
/* * * → → */
/* 3 2 5 4 1 6 + + 1 + 6 + 3 2 1 5 6 4 */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_upos($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch / 2;
if ($this->dup_a != 0)
exit("***error 交叉方法が不適当 (C_upos)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_upos)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p2];
// 交叉
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
$this->kou1[$i2] = (uniform() < 0.5) ? 0 : 1;
if ($this->kou1[$i2] > 0) {
$this->ind[$k1][$i2] = $this->ind[$p2][$i2];
$this->ind[$k2][$i2] = $this->ind[$p1][$i2];
}
}
$p = 0;
for ($i2 = 0; $i2 < $this->len[$p1]; $i2++) {
$sw = 0;
for ($i3 = 0; $i3 < $this->len[$p1] && $sw == 0; $i3++) {
if ($this->kou1[$i3] > 0 && $this->ind[$p1][$i2] == $this->ind[$k1][$i3])
$sw = 1;
}
if ($sw == 0) {
for ($i3 = $p; $i3 < $this->len[$p1] && $sw == 0; $i3++) {
if ($this->kou1[$i3] == 0) {
$this->ind[$k1][$i3] = $this->ind[$p1][$i2];
$p = $i3 + 1;
$sw = 1;
}
}
}
}
$p = 0;
for ($i2 = 0; $i2 < $this->len[$p2]; $i2++) {
$sw = 0;
for ($i3 = 0; $i3 < $this->len[$p2] && $sw == 0; $i3++) {
if ($this->kou1[$i3] > 0 && $this->ind[$p2][$i2] == $this->ind[$k2][$i3])
$sw = 1;
}
if ($sw == 0) {
for ($i3 = $p; $i3 < $this->len[$p2] && $sw == 0; $i3++) {
if ($this->kou1[$i3] == 0) {
$this->ind[$k2][$i3] = $this->ind[$p2][$i2];
$p = $i3 + 1;
$sw = 1;
}
}
}
}
}
}
}
/*******************************************************************/
/* 交叉(エッジ組み替え交叉.以下の手順に従って行う.対立遺伝子は */
/* 0~(max_len-1)である必要がある) */
/* (0) エッジマップを作成する.エッジマップとは,2つの親 */
/* を見て,ノードがどこに接続されているのかを表すもの */
/* であり,例えば,2つの親が, */
/* [A B C D E F] */
/* [B D C A E F] */
/* である場合は, */
/* A : B F C E */
/* B : A C D F */
/* C : B D A */
/* D : C E B */
/* E : D F A */
/* F : A E B */
/* となる. */
/* (1) 両親の2つの出発点の内1つで初期化する.ランダムま */
/* たはステップ(4)の基準に従って選ぶ(現在のノード) */
/* (2) エッジマップから,現在のノードを除く */
/* (3) 現在のノードが接続先のノードを持っていたら,(4)に */
/* 進む.さもなければ,(5)に進む */
/* (4) 現在のノードが持っている接続先ノードの内,最も少な */
/* い接続先ノードを持ったノードを選択し(同じ条件の場 */
/* 合は,ランダム),それを現在のノードとし,(2)に進む */
/* (5) 未接続のノードが残っていればランダムに選択し,(2)に */
/* 戻る.さもなければ,終了する */
/* kosa : 交叉確率 */
/* k_method : 選択方法 */
/* =-1 : ランダム(default) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* k_bias : α,または,method=2の場合は初期値(default=0) */
/* k_step : β(default=1) */
/*******************************************************************/
function C_edge($kosa, $k_method = -1, $k_bias = 0.0, $k_step = 1.0)
{
$e = array(2);
$k0 = 0;
/*
初期設定とデータのチェック
*/
$pair = $this->max_ch;
if ($this->dup_a != 0)
exit("***error 交叉方法が不適当 (C_edge)\n");
if ($this->min_len > 0)
exit("***error 遺伝子長は固定長でなければならない (C_edge)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $pair; $i1++) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(1, 1);
// 交叉する場合
else {
// 親の選択
$p1 = $this->Select($k_method, $k_bias, $k_step);
$sw = 0;
while ($sw == 0) {
$p2 = $this->Select($k_method, $k_bias, $k_step);
if ($p1 != $p2)
$sw = 1;
}
// 遺伝子長
$k = $this->Position(-1);
$this->pi_w[$k] = 1;
$this->len[$k] = $this->len[$p1];
// エッジマップの初期化
for ($i2 = 0; $i2 < $this->len[$k]; $i2++) {
$this->edge[$i2][0] = 0;
for ($i3 = 1; $i3 <= 4; $i3++)
$this->edge[$i2][$i3] = -1;
}
// 交叉
// エッジマップの作成
for ($i2 = 0; $i2 < $this->len[$k]; $i2++) {
$sw = 0;
for ($i3 = 0; $i3 < $this->len[$k] && $sw == 0; $i3++) {
if ($i2 == $this->ind[$p1][$i3]) {
$sw = 1;
if ($i3 == 0) {
$e[0] = $this->ind[$p1][$this->len[$k]-1];
$e[1] = $this->ind[$p1][1];
}
else {
if ($i3 == $this->len[$k]-1) {
$e[0] = $this->ind[$p1][$i3-1];
$e[1] = $this->ind[$p1][0];
}
else {
$e[0] = $this->ind[$p1][$i3-1];
$e[1] = $this->ind[$p1][$i3+1];
}
}
for ($i4 = 0; $i4 < 2; $i4++) {
$this->edge[$i2][0]++;
$this->edge[$i2][$this->edge[$i2][0]] = $e[$i4];
}
}
}
$sw = 0;
for ($i3 = 0; $i3 < $this->len[$k] && $sw == 0; $i3++) {
if ($i2 == $this->ind[$p2][$i3]) {
$sw = 1;
if ($i3 == 0) {
$e[0] = $this->ind[$p2][$this->len[$k]-1];
$e[1] = $this->ind[$p2][1];
}
else {
if ($i3 == $this->len[$k]-1) {
$e[0] = $this->ind[$p2][$i3-1];
$e[1] = $this->ind[$p2][0];
}
else {
$e[0] = $this->ind[$p2][$i3-1];
$e[1] = $this->ind[$p2][$i3+1];
}
}
for ($i4 = 0; $i4 < 2; $i4++) {
$sw = 1;
for ($i5 = 1; $i5 <= $this->edge[$i2][0] && $sw == 1; $i5++) {
if ($this->edge[$i2][$i5] == $e[$i4])
$sw = 2;
}
if ($sw == 1) {
$this->edge[$i2][0]++;
$this->edge[$i2][$this->edge[$i2][0]] = $e[$i4];
}
}
}
}
}
// 交叉の実行
// 出発点の決定
$k1 = $this->ind[$p1][0];
$k2 = $this->ind[$p2][0];
if ($this->edge[$k1][0] == $this->edge[$k2][0])
$kk = (uniform() > 0.5) ? $k2 : $k1;
else
$kk = ($this->edge[$k1][0] < $this->edge[$k2][0]) ? $k1 : $k2;
$this->ind[$k][0] = $kk;
$p = 1;
while ($p < $this->len[$k]) {
// ノードの除去
for ($i2 = 0; $i2 < $this->len[$k]; $i2++) {
$sw = 0;
if ($this->edge[$i2][0] > 0) {
for ($i3 = 1; $i3 <= 4 && $sw == 0; $i3++) {
if ($this->edge[$i2][$i3] == $kk) {
$sw = 1;
$this->edge[$i2][$i3] = -1;
$this->edge[$i2][0]--;
}
}
}
}
// 次の現在ノードの選択
$min = 10;
$num = 0;
for ($i2 = 1; $i2 <= 4; $i2++) {
if ($this->edge[$kk][$i2] >= 0) {
$k1 = $this->edge[$kk][$i2];
if ($this->edge[$k1][0] >= 0 && $this->edge[$k1][0] < $min) {
$num = 1;
$min = $this->edge[$k1][0];
$k0 = $k1;
}
else {
if ($this->edge[$k1][0] == $min)
$num++;
}
}
}
if ($num > 1) {
$k1 = intval(uniform() * $num) + 1;
if ($k1 > $num)
$k1 = $num;
$k2 = 0;
$k0 = -1;
for ($i2 = 1; $i2 <= 4 && $k0 < 0; $i2++) {
if ($this->edge[$kk][$i2] >= 0) {
if ($this->edge[$this->edge[$kk][$i2]][0] == $min) {
$k2++;
if ($k1 == $k2)
$k0 = $this->edge[$kk][$i2];
}
}
}
}
else {
if ($num <= 0) {
$num = 0;
for ($i2 = 0; $i2 < $this->len[$k]; $i2++) {
if ($i2 != $kk && $this->edge[$i2][0] >= 0)
$num++;
}
if ($num <= 0)
exit("***error invalid data (C_edge)\n");
else {
$k1 = intval(uniform() * $num) + 1;
if ($k1 > $num)
$k1 = $num;
$k2 = 0;
$k0 = -1;
for ($i2 = 0; $i2 < $this->len[$k] && $k0 < 0; $i2++) {
if ($i2 != $kk && $this->edge[$i2][0] >= 0) {
$k2++;
if ($k1 == $k2)
$k0 = $i2;
}
}
}
}
}
$this->edge[$kk][0] = -1;
$this->ind[$k][$p] = $k0;
$kk = $k0;
$p++;
}
}
}
}
/*************************************************************/
/* 交叉(サブツアー交叉.2点交叉の拡張である.ただし,相手に*/
/* 同じ遺伝子のグループがない限り実行されない.たとえば*/
/* ***abcd** */
/* *cdab**** */
/* のような両親の時実行され,以下の4つの子供が生成され*/
/* る) */
/* ***cdab** */
/* *abcd**** */
/* ***badc** */
/* *dcba**** */
/* 最大,4*交叉回数*個体総数*(個体総数-1) 個の子 */
/* 供が生成される可能性があるので,子供の数としてこの値*/
/* 以上のデータを入力しておく必要がある. */
/* kosa : 交叉確率 */
/* count : 1つのペアーに対する交差回数(default=10) */
/*************************************************************/
function C_sub($kosa, $count = 10)
{
$t22 = 0;
/*
初期設定とデータのチェック
*/
if ((4*$count*$this->size*($this->size-1)) > $this->max_ch)
exit("***error 子供が多すぎる (C_sub)\n");
/*
交叉
*/
for ($i1 = 0; $i1 < $this->size-1; $i1++) {
// 親1
$p1 = $this->Position($i1);
if ($p1 >= 0) {
for ($i2 = $i1; $i2 < $this->size; $i2++) {
// 親2
$p2 = $this->Position($i2);
if ($p2 >= 0) {
// 交叉しない場合
if (uniform() > $kosa)
$this->C_copy(2, 1);
// 交叉する場合
else {
// 交叉回数の制御
for ($i3 = 0; $i3 < $count; $i3++) {
// 交叉位置の決定(点の後ろで交叉)
// 親1の交叉位置
$t11 = intval(uniform() * $this->len[$p1]);
if ($t11 > ($this->len[$p1]-1))
$t11 = $this->len[$p1] - 1;
$sw = 0;
while ($sw == 0) {
$t12 = intval(uniform() * $this->len[$p1]);
if ($t12 > ($this->len[$p1]-1))
$t12 = $this->len[$p1] - 1;
if ($t12 != $t11)
$sw = 1;
}
if ($t11 > $t12) {
$k1 = $t11;
$t11 = $t12;
$t12 = $k1;
}
// 親2の交叉位置
$sw = 0;
$t21 = -1;
for ($i4 = 0; $i4 < $this->len[$p2] && $t21 < 0; $i4++) {
for ($i5 = $t11; $i5 <= $t12 && $t21 < 0; $i5++) {
if ($this->ind[$p2][$i4] == $this->ind[$p1][$i5])
$t21 = $i4;
}
}
if ($t21 >= 0) {
$t22 = $t21 + $t12 - $t11;
if ($t22 < $this->len[$p2]) {
$sw = 1;
for ($i4 = $t21+1; $i4 <= $t22 && $sw > 0; $i4++) {
$sw = 0;
for ($i5 = $t11; $i5 <= $t12 && $sw == 0; $i5++) {
if ($this->ind[$p2][$i4] == $this->ind[$p1][$i5])
$sw = 1;
}
}
}
}
// 交叉の実行
if ($sw > 0) {
$k1 = $this->Position(-1);
$this->pi_w[$k1] = 1;
$this->len[$k1] = $this->len[$p1];
$k2 = $this->Position(-1);
$this->pi_w[$k2] = 1;
$this->len[$k2] = $this->len[$p1];
$k3 = $this->Position(-1);
$this->pi_w[$k3] = 1;
$this->len[$k3] = $this->len[$p2];
$k4 = $this->Position(-1);
$this->pi_w[$k4] = 1;
$this->len[$k4] = $this->len[$p2];
for ($i4 = 0; $i4 < $t11; $i4++) {
$this->ind[$k1][$i4] = $this->ind[$p1][$i4];
$this->ind[$k2][$i4] = $this->ind[$p1][$i4];
}
for ($i4 = $t11; $i4 <= $t12; $i4++) {
$this->ind[$k1][$i4] = $this->ind[$p2][$t21+$i4-$t11];
$this->ind[$k2][$i4] = $this->ind[$p2][$t22-$i4+$t11];
}
for ($i4 = $t12+1; $i4 < $this->len[$p1]; $i4++) {
$this->ind[$k1][$i4] = $this->ind[$p1][$i4];
$this->ind[$k2][$i4] = $this->ind[$p1][$i4];
}
for ($i4 = 0; $i4 < $t21; $i4++) {
$this->ind[$k3][$i4] = $this->ind[$p2][$i4];
$this->ind[$k4][$i4] = $this->ind[$p2][$i4];
}
for ($i4 = $t21; $i4 <= $t22; $i4++) {
$this->ind[$k3][$i4] = $this->ind[$p1][$t11+$i4-$t21];
$this->ind[$k4][$i4] = $this->ind[$p1][$t12-$i4+$t21];
}
for ($i4 = $t22+1; $i4 < $this->len[$p2]; $i4++) {
$this->ind[$k3][$i4] = $this->ind[$p2][$i4];
$this->ind[$k4][$i4] = $this->ind[$p2][$i4];
}
}
}
}
}
}
}
}
}
/**************************************/
/* 突然変異(対立遺伝子との置き換え) */
/* pr : 突然変異率 */
/**************************************/
function M_alle($pr)
{
/*
データのチェックと初期設定
*/
if ($this->dup_a == 0)
exit("***error 突然変異方法が不適当 (M_alle)\n");
/*
実行
*/
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1) {
for ($i2 = 0; $i2 < $this->len[$i1]; $i2++) {
if (uniform() <= $pr) {
$lid = intval(uniform() * ($this->allele_u - $this->allele_l + 1) + $this->allele_l);
if ($lid > $this->allele_u)
$lid = $this->allele_u;
if ($lid != $this->ind[$i1][$i2])
$this->ind[$i1][$i2] = $lid;
}
}
}
}
}
/**********************************************************************/
/* 突然変異(移動.2点を選択し,2番目の遺伝子を1番目の遺伝子の前に */
/* 移動する) */
/* pr : 突然変異率 */
/**********************************************************************/
function M_move($pr)
{
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
/*
位置の決定
*/
// p1
$p1 = intval(uniform() * $this->len[$i1]);
if ($p1 >= $this->len[$i1])
$p1 = $this->len[$i1] - 1;
// p2
$sw = 0;
while ($sw == 0) {
$p2 = intval(uniform() * $this->len[$i1]);
if ($p2 >= $this->len[$i1])
$p2 = $this->len[$i1] - 1;
if ($p2 != $p1)
$sw = 1;
}
/*
実行
*/
if ($p2 > $p1) {
$ld = $this->ind[$i1][$p2];
for ($i2 = $p2; $i2 > $p1; $i2--)
$this->ind[$i1][$i2] = $this->ind[$i1][$i2-1];
$this->ind[$i1][$p1] = $ld;
}
else {
$ld = $this->ind[$i1][$p2];
for ($i2 = $p2; $i2 < $p1-1; $i2++)
$this->ind[$i1][$i2] = $this->ind[$i1][$i2+1];
$this->ind[$i1][$p1-1] = $ld;
}
}
}
}
/********************************************************/
/* 突然変異(逆位.2点間の遺伝子順序を逆に並べ替える) */
/* pr : 突然変異率 */
/* wd : >0 : 幅を固定 */
/* =0 : 幅をランダム(default) */
/********************************************************/
function M_inv($pr, $wd = 0)
{
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
/*
区間の決定
*/
if ($wd == 0) {
$p1 = intval(uniform() * $this->len[$i1]);
if ($p1 >= $this->len[$i1])
$p1 = $this->len[$i1] - 1;
$sw = 0;
while ($sw == 0) {
$p2 = intval(uniform() * $this->len[$i1]);
if ($p2 >= $this->len[$i1])
$p2 = $this->len[$i1] - 1;
if ($p2 != $p1)
$sw = 1;
}
if ($p1 > $p2) {
$p = $p1;
$p1 = $p2;
$p2 = $p;
}
}
else {
$p1 = $this->len[$i1];
while ($p1 > $this->len[$i1]-2)
$p1 = intval(uniform() * $this->len[$i1]);
$p2 = $p1 + $wd - 1;
if ($p2 >= $this->len[$i1])
$p2 = $this->len[$i1] - 1;
}
/*
実行
*/
$sw = 0;
while ($sw == 0) {
$lid = $this->ind[$i1][$p1];
$this->ind[$i1][$p1] = $this->ind[$i1][$p2];
$this->ind[$i1][$p2] = $lid;
$p1++;
$p2--;
if ($p1 >= $p2)
$sw = 1;
}
}
}
}
/**********************************************************************/
/* 突然変異(スクランブル.2点間の遺伝子順序をランダムに並べ替える) */
/* pr : 突然変異率 */
/* wd : >0 : 幅を固定 */
/* =0 : 幅をランダム(default) */
/**********************************************************************/
function M_scram($pr, $wd = 0)
{
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
/*
区間の決定
*/
if ($wd == 0) {
$p1 = intval(uniform() * $this->len[$i1]);
if ($p1 >= $this->len[$i1])
$p1 = $this->len[$i1] - 1;
$sw = 0;
while ($sw == 0) {
$p2 = intval(uniform() * $this->len[$i1]);
if ($p2 >= $this->len[$i1])
$p2 = $this->len[$i1] - 1;
if ($p2 != $p1)
$sw = 1;
}
if ($p1 > $p2) {
$p = $p1;
$p1 = $p2;
$p2 = $p;
}
}
else {
$p1 = $this->len[$i1];
while ($p1 > $this->len[$i1]-2)
$p1 = intval(uniform() * $this->len[$i1]);
$p2 = $p1 + $wd - 1;
if ($p2 >= $this->len[$i1])
$p2 = $this->len[$i1] - 1;
}
/*
実行
*/
for ($i2 = $p1; $i2 <= $p2; $i2++) {
$p = intval(uniform() * ($p2 - $p1 + 1) + $p1);
if ($p > $p2)
$p = $p2;
$ld = $this->ind[$i1][$i2];
$this->ind[$i1][$i2] = $this->ind[$i1][$p];
$this->ind[$i1][$p] = $ld;
}
}
}
}
/**********************************************************************/
/* 突然変異(転座.2点間の遺伝子を他の位置のものと置き換える.ただし */
/* 重複部分はそのままとする) */
/* pr : 突然変異率 */
/* wd : >0 : 幅を固定 */
/* =0 : 幅をランダム(default) */
/**********************************************************************/
function M_chg($pr, $wd = 0)
{
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
/*
区間等の決定([p1,p2]と[p3,p4]の入れ替え)
*/
// p1
$p1 = intval(uniform() * $this->len[$i1]);
if ($p1 >= $this->len[$i1])
$p1 = $this->len[$i1] - 1;
// p3
$sw = 0;
while ($sw == 0) {
$p3 = intval(uniform() * $this->len[$i1]);
if ($p3 >= $this->len[$i1])
$p3 = $this->len[$i1] - 1;
if ($p3 != $p1)
$sw = 1;
}
// 小さい方をp1,p2にする
if ($p1 > $p3) {
$p = $p1;
$p1 = $p3;
$p3 = $p;
}
// p4, p2
$p4 = ($wd == 0) ? intval(uniform() * ($this->len[$i1] - $p3)) + $p3 : $p1 + $wd - 1;
if ($p4 >= $this->len[$i1])
$p4 = $this->len[$i1] - 1;
$p2 = $p1 + ($p4 - $p3);
// 重複部分のチェック
if ($p2 >= $p3) {
$p = $p3 - 1;
$p3 = $p2 + 1;
$p2 = $p;
$p4 = $p3 + ($p2 - $p1);
}
/*
実行
*/
$p = $p3;
for ($i2 = $p1; $i2 <= $p2; $i2++) {
$ld = $this->ind[$i1][$i2];
$this->ind[$i1][$i2] = $this->ind[$i1][$p];
$this->ind[$i1][$p] = $ld;
$p++;
}
}
}
}
/**********************************************************************/
/* 突然変異(重複.2点間の遺伝子を他の位置にコピーする */
/* pr : 突然変異率 */
/* wd : >0 : 幅を固定 */
/* =0 : 幅をランダム(deafult) */
/**********************************************************************/
function M_dup($pr, $wd = 0)
{
/*
データのチェック
*/
if ($this->dup_a == 0)
exit("***error 突然変異方法が不適当 (M_dup)\n");
/*
実行
*/
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
// 区間の決定([p1,p2]を[p3,p4]にコピー)
// p1
$p1 = intval(uniform() * $this->len[$i1]);
if ($p1 >= $this->len[$i1])
$p1 = $this->len[$i1] - 1;
// p3
$sw = 0;
while ($sw == 0) {
$p3 = intval(uniform() * $this->len[$i1]);
if ($p3 >= $this->len[$i1])
$p3 = $this->len[$i1] - 1;
if ($p3 != $p1)
$sw = 1;
}
// 区間を決める
if ($p3 > $p1) {
$p4 = ($wd == 0) ? intval(uniform() * ($this->len[$i1] - $p3)) + $p3 : $p3 + $wd - 1;
if ($p4 >= $this->len[$i1])
$p4 = $this->len[$i1] - 1;
$p2 = $p1 + ($p4 - $p3);
}
else {
$p2 = ($wd == 0) ? intval(uniform() * ($this->len[$i1] - $p1)) + $p1 :$p1 + $wd - 1;
if ($p2 >= $this->len[$i1])
$p2 = $this->len[$i1] - 1;
$p4 = $p3 + ($p2 - $p1);
}
// 実行
$p = $p4;
for ($i2 = $p2; $i2 >= $p1; $i2--) {
$this->ind[$i1][$p] = $this->ind[$i1][$i2];
$p--;
}
}
}
}
/******************************************************/
/* 突然変異(摂動.値をある量だけ変化させる) */
/* pr : 突然変異率 */
/* method : =0 : 正規分布(default) */
/* =1 : 一様分布 */
/* m : 平均または一様分布の下限(default=0.0) */
/* s : 標準偏差または一様分布の上限(default=1.0) */
/******************************************************/
function M_per($pr, $method = 0, $m = 0.0, $s = 1.0)
{
$wd = 0.0;
/*
データのチェックと初期設定
*/
if ($this->dup_a == 0)
exit("***error 突然変異方法が不適当 (M_per)\n");
if ($method > 0)
$wd = $s - $m;
/*
実行
*/
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1) {
for ($i2 = 0; $i2 < $this->len[$i1]; $i2++) {
if (uniform() <= $pr) {
if ($method == 0)
$w = norm_d($m, $s);
else {
$w = uniform() * $wd;
if (uniform() < 0.5)
$w = -$w;
}
$x1 = (double)$this->ind[$i1][$i2] + $w;
if ($x1 > $this->allele_u)
$x1 = $this->allele_u;
else {
if ($x1 < $this->allele_l)
$x1 = $this->allele_l;
}
$this->ind[$i1][$i2] = intval($x1);
}
}
}
}
}
/**********************************************************************/
/* 突然変異(挿入.ある長さの遺伝子を挿入する) */
/* pr : 突然変異率 */
/* wd : >0 : 幅を固定 */
/* =0 : 幅をランダム(default) */
/**********************************************************************/
function M_ins($pr, $wd = 0)
{
/*
データのチェック
*/
if ($this->dup_a == 0 || $this->min_len < 0)
exit("***error 突然変異方法が不適当 (M_ins)\n");
/*
実行
*/
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
// 挿入位置の決定
$p = intval(uniform() * ($this->len[$i1]+1));
if ($p > $this->len[$i1])
$p = $this->len[$i1];
// 挿入する遺伝子長の決定
$l = ($wd == 0) ? intval(uniform() * ($this->max_len - $this->len[$i1] + 1)) : $wd;
if ($l > $this->max_len-$this->len[$i1])
$l = $this->max_len - $this->len[$i1];
else {
if ($l <= 0)
$l = 1;
}
// 実行
// 挿入場所の確保
if ($p < $this->len[$i1]) {
for ($i2 = $this->len[$i1]+$l-1; $i2 >= $p; $i2--)
$this->ind[$i1][$i2] = $this->ind[$i1][$i2-$l];
}
// 挿入場所の遺伝子の決定
for ($i2 = $p; $i2 < $p+$l; $i2++) {
$ld = intval(uniform() * ($this->allele_u - $this->allele_l + 1) + $this->allele_l);
if ($ld > $this->allele_u)
$ld = $this->allele_u;
$this->ind[$i1][$i2] = $ld;
}
$this->len[$i1] += $l;
}
}
}
/**********************************************************************/
/* 突然変異(削除.ある長さの遺伝子を削除する) */
/* pr : 突然変異率 */
/* wd : >0 : 幅を固定 */
/* =0 : 幅をランダム(default) */
/**********************************************************************/
function M_del($pr, $wd = 0)
{
/*
データのチェック
*/
if ($this->dup_a == 0 || $this->min_len < 0)
exit("***error 突然変異方法が不適当 (M_del)\n");
exit(1);
/*
実行
*/
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1 && uniform() <= $pr) {
// 削除位置の決定
$p = intval(uniform() * $this->len[$i1]);
if ($p >= $this->len[$i1])
$p = $this->len[$i1] - 1;
// 削除する遺伝子長の決定
$max = ($this->len[$i1]-$this->min_len < $this->len[$i1]-$p) ? $this->len[$i1] - $this->min_len : $this->len[$i1] - $p;
$l = ($wd == 0) ? intval(uniform() * $max + 1) : $wd;
if ($l > $max)
$l = $max;
// 実行
for ($i2 = 0; $i2 < $this->len[$i1]-$p-$l; $i2++)
$this->ind[$i1][$p+$i2] = $this->ind[$i1][$p+$i2+$l];
$this->len[$i1] -= $l;
}
}
}
/*********************************************************************/
/* 淘汰(エリート・ルーレット選択) */
/* elite : エリートで残す個体数(default=0) */
/* s_method : ルーレット板の作成方法(default=1) */
/* =0 : 適応度をそのまま使用 */
/* =1 : 最小値からの差(ただし,α以下の場合はα) */
/* =2 : 評価値に順位をつけ,減少率βで線形化 */
/* s_bias : α,または,method=2の場合は初期値(default=0) */
/* s_step : β(default=1) */
/*********************************************************************/
function S_roul($elite = 0, $s_method = 1, $s_bias = 0.0, $s_step = 1.0)
{
$count = 0;
$k = 0;
$n = 0;
/*
値のチェックと初期設定
*/
if ($s_method != 0 && $s_method != 2)
$s_method = 1;
if ($elite > $this->size)
exit("***error エリートで残す数が多すぎる (S_roul)\n");
if ($s_method == 2 && $s_step <= 0.0)
$s_step = 1.0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++)
$this->s_w[$i1] = 0;
/*
重複個体を削除
*/
if ($this->dup_s == 0) {
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 0) {
for ($i2 = $i1+1; $i2 < $this->size+$this->max_ch; $i2++) {
if ($this->pi_w[$i2] > 0 && $this->len[$i1] == $this->len[$i2]) {
$sw = 0;
for ($i3 = 0; $i3 < $this->len[$i1] && $sw == 0; $i3++) {
if ($this->ind[$i1][$i3] != $this->ind[$i2][$i3])
$sw = 1;
}
if ($sw == 0)
$this->pi_w[$i2] = 0;
}
}
}
}
}
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1)
$n++;
}
if ($n < 0 || $this->dup_s == 0 && $n < $this->size)
exit("***error 残す個体がない (S_roul)\n");
/*
淘汰して残す個体を選ぶ
*/
// エリートの選択
$sw = 0;
while ($k < $elite && $k < $n && $sw == 0) {
$max = -1;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] > 1 && $this->s_w[$i1] == 0) {
if ($max < 0 || $this->pi[$i1] > $this->pi[$max])
$max = $i1;
}
}
if ($max < 0)
$sw = 1;
else {
$this->s_w[$max] = 1;
$k++;
}
}
// ルーレット選択
while ($count < $this->size+$this->max_ch && $k < $this->size) {
$p = $this->Select($s_method, $s_bias, $s_step);
if ($this->dup_s == 0 && $this->s_w[$p] > 0)
$count++;
else {
$count = 0;
$this->s_w[$p]++;
$k++;
}
}
// 選択に失敗した場合の処理
if ($this->dup_s == 0 && $k < $this->size) {
for ($i1 = 0; $i1 < $this->size+$this->max_ch && $k < $this->size; $i1++) {
if ($this->pi_w[$i1] > 1 && $this->s_w[$i1] == 0) {
$this->s_w[$i1] = 1;
$k++;
}
}
}
// 複数回選択されたものの処理
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->s_w[$i1] == 0)
$this->pi_w[$i1] = 0;
}
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->s_w[$i1] > 0) {
if ($this->s_w[$i1] > 1) {
for ($i2 = 2; $i2 <= $this->s_w[$i1]; $i2++) {
$k = $this->Position(-1);
$this->len[$k] = $this->len[$i1];
$this->pi_w[$k] = 2;
$this->pi[$k] = $this->pi[$i1];
for ($i3 = 0; $i3 < $this->len[$i1]; $i3++)
$this->ind[$k][$i3] = $this->ind[$i1][$i3];
}
}
}
}
}
}
/***********************************/
/* [0, 1] 区間の一様分布変量の発生 */
/***********************************/
function uniform()
{
return mt_rand() / mt_getrandmax();
}
/***********************************/
/* 正規分布変量の発生 */
/* m : 平均 */
/* s : 標準偏差 */
/* return : 正規分布変量 */
/***********************************/
function norm_d($m, $s)
{
$x = 0.0;
for ($i1 = 0; $i1 < 12; $i1++)
$x += uniform();
$x = $s * ($x - 6.0) + $m;
return $x;
}
/*******************/
/* クラスTSPの定義 */
/*******************/
class TSP extends Species {
private $max_gen; // 最大世代交代数
private $kosa_m; // 交叉方法
// =-1 : 交叉を使用しない
// =0 : 親のコピー
// =1 : 循環交叉
// =2 : 部分的交叉
// =3 : 順序交叉
// =4 : 一様順序交叉
// =5 : 一様位置交叉
// =6 : エッジ組み替え交叉
// =7 : サブツアー交叉
private $kosa; // 交叉確率
private $k_point; // 交差点の数(負の時は,1から-k_point間のランダム)
private $k_vr; // =0 : 両親とも同じ位置で交叉
// =1 : 両親が異なる位置で交叉(遺伝子長は可変)
private $k_method; // 交叉の時の親の選択方法
// =-1 : ランダム
// =0 : 適応度をそのまま使用
// =1 : 最小値からの差(ただし,α以下の場合はα)
// =2 : 評価値に順位をつけ,減少率βで線形化
private $k_bias; // α,または,method=2の場合は初期値
private $k_step; // β
private $mute_m; // 突然変異方法
// =-1 : 突然変異を使用しない
// =0 : 移動
// =1 : 逆位
// =2 : スクランブル
// =3 : 転座
private $mute; // 突然変異率
private $wd; // 突然変異に使用する部分遺伝子長
private $m_mean; // 摂動の平均値
private $m_std; // 摂動の標準偏差
private $elite; // エリート選択で残す数
private $s_method; // ルーレット板の作成方法
// =0 : 適応度をそのまま使用
// =1 : 最小値からの差(ただし,α以下の場合はα)
// =2 : 評価値に順位をつけ,減少率βで線形化
private $s_bias; // α,または,s_method=2の場合は初期値
private $s_step; // β
private $out_d; // 表示間隔
private $out_lvl; // 出力レベル
// =0 : 最終出力だけ
// n>0 : n世代毎に出力(負の時はファイル)
private $out_m; // 出力方法
// =0 : すべてを出力
// =1 : 最大適応度の個体だけを出力
private $o_file; // 出力ファイル名
private $city; //都市の位置データ
private $n_city; // 都市の数
private $rg; // 都市間の距離
private $kinbo; // 近傍探索(0:行わない,1:行う)
private $neib; // 近傍(2 or 3)
private $sel; // エッジの選択方法
// =0 : 最良のものを選択
// =1 : 最初のものを選択
/***************************************/
/* コンストラクタ */
/* name1 : Species定義ファイル名 */
/* name2 : TSP定義ファイル名 */
/* seed : 乱数の初期値 */
/***************************************/
function TSP ($name1, $name2, $seed)
{
parent::Species($name1, $seed);
// 基本データの入力
$in = fopen($name2, "r");
fscanf($in, "%*s %d %*s %d", $this->out_lvl, $this->out_m);
fscanf($in, "%*s %s %*s %d", $this->o_file, $this->out_d);
fscanf($in, "%*s %d %*s %lf %*s %d %*s %d %*s %d %*s %lf %*s %lf", $this->kosa_m, $this->kosa, $this->k_point, $this->k_vr, $this->k_method, $this->k_bias, $this->k_step);
fscanf($in, "%*s %d %*s %lf %*s %d %*s %lf %*s %lf", $this->mute_m, $this->mute, $this->wd, $this->m_mean, $this->m_stds);
fscanf($in, "%*s %d %*s %d %*s %lf %*s %lf", $this->elite, $this->s_method, $this->s_bias, $this->s_step);
fscanf($in, "%*s %d %*s %d", $this->n_city, $this->max_gen);
fscanf($in, "%*s %d %*s %d", $this->kinbo, $this->neib);
fscanf($in, "%*s %d", $this->sel);
if ($this->kinbo > 0 && $this->neib != 2 && $this->neib != 3)
exit("***error 近傍の値が不適当 \n");
if ($this->n_city != $this->max_len)
exit("***error 都市数が不適当 \n");
// 都市の位置データ
$this->city = array($this->n_city);
for ($i1 = 0; $i1 < $this->n_city; $i1++) {
$this->city[$i1] = array(2);
fscanf($in, "%d %d", $this->city[$i1][0], $this->city[$i1][1]);
}
// 距離テーブル
$this->rg = array($this->n_city);
for ($i1 = 0; $i1 < $this->n_city; $i1++) {
$this->rg[$i1] = array($this->n_city);
for ($i2 = $i1+1; $i2 < $this->n_city; $i2++) {
$x = $this->city[$i2][0] - $this->city[$i1][0];
$y = $this->city[$i2][1] - $this->city[$i1][1];
$this->rg[$i1][$i2] = round(sqrt($x * $x + $y * $y));
}
}
for ($i1 = 1; $i1 < $this->n_city; $i1++) {
for ($i2 = 0; $i2 < $i1; $i2++)
$this->rg[$i1][$i2] = $this->rg[$i2][$i1];
}
fclose($in);
}
/**************/
/* 全体の制御 */
/**************/
function Control()
{
$gen = 1;
// 初期集団の発生
$this->Init_std();
// 評価
if ($this->kinbo > 0)
$this->Kinbo();
else
$this->Adap();
// 出力
printf("***世代 %d 適応度 max %f (%d) mean %f\n", $gen, $this->max, $this->max_n, $this->mean);
if (abs($this->out_lvl) > 0)
$this->Output($gen);
// 世代交代
for ($gen = 2; $gen <= $this->max_gen; $gen++) {
// 交叉
switch ($this->kosa_m) {
case -1:
break;
case 0:
$this->C_copy(); // 親のコピー
break;
case 1:
$this->C_cycle($this->kosa); // 循環交叉
break;
case 2:
$this->C_part($this->kosa); // 部分的交叉
break;
case 3:
$this->C_seq($this->kosa); // 順序交叉
break;
case 4:
$this->C_useq($this->kosa); // 一様順序交叉
break;
case 5:
$this->C_upos($this->kosa); // 一様位置交叉
break;
case 6:
$this->C_edge($this->kosa); // エッジ組み替え交叉
break;
case 7:
$this->C_sub($this->kosa, $this->k_point); // サブツアー交叉
break;
default:
break;
}
// 突然変異
switch ($this->mute_m) {
case -1:
break;
case 0:
$this->M_move($this->mute); // 移動
break;
case 1:
$this->M_inv($this->mute); // 逆位
break;
case 2:
$this->M_scram($this->mute); // スクランブル
break;
case 3:
$this->M_chg($this->mute); // 転座
break;
default:
break;
}
// 適応度
if ($this->kinbo > 0)
$this->Kinbo();
else
$this->Adap();
// 淘汰
$this->S_roul($this->elite);
// 出力
if ($gen%$this->out_d == 0)
printf("***世代 %d 適応度 max %f (%d) mean %f\n", $gen, $this->max, $this->max_n, $this->mean);
if (abs($this->out_lvl) > 0) {
if ($gen%abs($this->out_lvl) == 0)
$this->Output($gen);
}
}
$gen--;
$k1 = $this->out_m;
$this->out_m = 0;
printf("***世代 %d 適応度 max %f (%d) mean %f\n", $gen, $this->max, $this->max_n, $this->mean);
$this->Output($gen);
$this->out_m = $k1;
}
/*********************************/
/* 距離の計算 */
/* n_c : 都市の数 */
/* p : 都市番号 */
/* return : 距離(負) */
/*********************************/
function Kyori($n_c, $p)
{
$range = 0;
$n1 = $p[0];
for ($i1 = 1; $i1 < $n_c; $i1++) {
$n2 = $p[$i1];
$range -= $this->rg[$n1][$n2];
$n1 = $n2;
}
$n2 = $p[0];
$range -= $this->rg[$n1][$n2];
return $range;
}
/****************/
/* 適応度の計算 */
/****************/
function Adap()
{
$k = 0;
$this->mean = 0.0;
$this->max = 0.0;
$this->max_n = -1;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1) {
$this->pi_w[$i1] = 2;
$this->pi[$i1] = $this->Kyori($this->len[$i1], $this->ind[$i1]);
}
if ($this->pi_w[$i1] > 0) {
$k++;
$this->mean += $this->pi[$i1];
if ($this->max_n < 0 || $this->pi[$i1] > $this->max) {
$this->max = $this->pi[$i1];
$this->max_n = $i1;
}
}
}
if ($k > 0)
$this->mean /= $k;
}
/**************************************/
/* エッジの入れ替え */
/* n_city : 都市の数 */
/* seq : 訪問する順番 */
/* r_m : 距離の負値 */
/* return : =0 : 改善がなかった */
/* =1 : 改善があった */
/**************************************/
function Change($n_city, &$seq, &$r_m)
{
$ch = 0;
$sw = 0;
$max = $r_m;
$n3 = intval(uniform() * ($n_city - 2));
if ($n3 > $n_city-3)
$n3 = $n_city - 3;
// 2近傍
for ($i1 = 0; $i1 <= $n_city-3 && $ch == 0; $i1++) {
if ($n3 == 0)
$n1 = $n_city - 2;
else
$n1 = $n_city - 1;
for ($i2 = $n3+2; $i2 <= $n1 && $ch == 0; $i2++) {
// 枝の場所((n3,n3+1), (k1,k2))
$k1 = $i2;
if ($i2 == $n_city-1)
$k2 = 0;
else
$k2 = $i2 + 1;
// 枝の入れ替え
$this->kou1[0] = $seq[$n3];
$k = 1;
for ($i3 = $k1; $i3 >= $n3+1; $i3--) {
$this->kou1[$k] = $seq[$i3];
$k++;
}
$nn = $k2;
while ($nn != $n3) {
$this->kou1[$k] = $seq[$nn];
$k++;
$nn++;
if ($nn > $n_city-1)
$nn = 0;
}
// 評価
$r = $this->Kyori($n_city, $this->kou1);
if ($r > $max) {
$max = $r;
$sw = 1;
for ($i3 = 0; $i3 < $n_city; $i3++)
$this->kou2[$i3] = $this->kou1[$i3];
if ($this->sel > 0)
$ch = 1;
}
}
$n3++;
if ($n3 > $n_city-3)
$n3 = 0;
}
// 3近傍
if ($this->neib == 3 && $ch == 0) {
for ($i1 = 0; $i1 <= $n_city-3 && $ch == 0; $i1++) {
$n1 = $n_city - 2;
$n2 = $n_city - 1;
for ($i2 = $n3+1; $i2 <= $n1 && $ch == 0; $i2++) {
for ($i3 = $i2+1; $i3 <= $n2 && $ch == 0; $i3++) {
// 枝の場所((n3,n3+1), ($i2,$i2+1), (k1,k2))
$k1 = $i3;
if ($i3 == $n_city-1)
$k2 = 0;
else
$k2 = $i3 + 1;
// 枝の入れ替えと評価
// 入れ替え(その1)
$this->kou1[0] = $seq[$n3];
$k = 1;
for ($i4 = $i2; $i4 >= $n3+1; $i4--) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
for ($i4 = $k1; $i4 >= $i2+1; $i4--) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
$nn = $k2;
while ($nn != $n3) {
$this->kou1[$k] = $seq[$nn];
$k++;
$nn++;
if ($nn > $n_city-1)
$nn = 0;
}
// 評価(その1)
$r = $this->Kyori($n_city, $this->kou1);
if ($r > $max) {
$max = $r;
$sw = 1;
for ($i3 = 0; $i3 < $n_city; $i3++)
$this->kou2[$i3] = $this->kou1[$i3];
if ($this->sel > 0)
$ch = 1;
}
// 入れ替え(その2)
$this->kou1[0] = $seq[$n3];
$k = 1;
for ($i4 = $k1; $i4 >= $i2+1; $i4--) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
for ($i4 = $n3+1; $i4 <= $i2; $i4++) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
$nn = $k2;
while ($nn != $n3) {
$this->kou1[$k] = $seq[$nn];
$k++;
$nn++;
if ($nn > $n_city-1)
$nn = 0;
}
// 評価(その2)
$r = $this->Kyori($n_city, $this->kou1);
if ($r > $max) {
$max = $r;
$sw = 1;
for ($i3 = 0; $i3 < $n_city; $i3++)
$this->kou2[$i3] = $this->kou1[$i3];
if ($this->sel > 0)
$ch = 1;
}
// 入れ替え(その3)
$this->kou1[0] = $seq[$n3];
$k = 1;
for ($i4 = $i2+1; $i4 <= $k1; $i4++) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
for ($i4 = $i2; $i4 >= $n3+1; $i4--) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
$nn = $k2;
while ($nn != $n3) {
$this->kou1[$k] = $seq[$nn];
$k++;
$nn++;
if ($nn > $n_city-1)
$nn = 0;
}
// 評価(その3)
$r = $this->Kyori($n_city, $this->kou1);
if ($r > $max) {
$max = $r;
$sw = 1;
for ($i3 = 0; $i3 < $n_city; $i3++)
$this->kou2[$i3] = $this->kou1[$i3];
if ($this->sel > 0)
$ch = 1;
}
// 入れ替え(その4)
$this->kou1[0] = $seq[$n3];
$k = 1;
for ($i4 = $i2+1; $i4 <= $k1; $i4++) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
for ($i4 = $n3+1; $i4 <= $i2; $i4++) {
$this->kou1[$k] = $seq[$i4];
$k++;
}
$nn = $k2;
while ($nn != $n3) {
$this->kou1[$k] = $seq[$nn];
$k++;
$nn++;
if ($nn > $n_city-1)
$nn = 0;
}
// 評価(その4)
$r = $this->Kyori($n_city, $this->kou1);
if ($r > $max) {
$max = $r;
$sw = 1;
for ($i3 = 0; $i3 < $n_city; $i3++)
$this->kou2[$i3] = $this->kou1[$i3];
if ($this->sel > 0)
$ch = 1;
}
}
}
$n3++;
if ($n3 > $n_city-3)
$n3 = 0;
}
}
// 設定
if ($sw > 0) {
$r_m = $max;
for ($i1 = 0; $i1 < $n_city; $i1++)
$seq[$i1] = $this->kou2[$i1];
}
return $sw;
}
/**************/
/* 近傍の探索 */
/**************/
function Kinbo()
{
$k = 0;
$this->max = 0.0;
$this->max_n = -1;
$this->mean = 0.0;
for ($i1 = 0; $i1 < $this->size+$this->max_ch; $i1++) {
if ($this->pi_w[$i1] == 1) {
$this->pi_w[$i1] = 2;
$sw = 1;
$r = $this->Kyori($this->len[$i1], $this->ind[$i1]);
while ($sw > 0)
$sw = $this->Change($this->len[$i1], $this->ind[$i1], $r);
$this->pi[$i1] = $r;
}
if ($this->pi_w[$i1] > 0) {
$k++;
$this->mean += $this->pi[$i1];
if ($this->max_n < 0 || $this->pi[$i1] > $this->max) {
$this->max = $this->pi[$i1];
$this->max_n = $i1;
}
}
}
if ($k > 0)
$this->mean /= $k;
}
/*****************************/
/* 結果の出力 */
/* gen : 現在の世代番号 */
/*****************************/
function Output($gen)
{
$k = 0;
if ($this->out_lvl >= 0) {
printf(" 出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ");
fscanf(STDIN, "%d", $pr);
}
else
$pr = -1;
if ($pr != 0) {
// 出力先の決定と評価値の出力
if ($pr > 0) {
$out = STDOUT;
fgets(STDIN);
}
else {
$x = getdate();
$now = $x["hours"]."時".$x["minutes"]."分".$x["seconds"]."秒";
$out = fopen($this->o_file, "ab");
fwrite($out, "***世代 ".$gen." 適応度 max ".$this->max." (".$this->max_n.") mean ".$this->mean." 時間 ".$now."\n");
}
// 巡回順序の出力
if ($this->out_m == 0) {
for ($i1 = 0; $i1 < $this->len[$this->max_n]; $i1++) {
$n = $this->ind[$this->max_n][$i1];
fwrite($out, $n." ".$this->city[$n][0]." ".$this->city[$n][1]."\n");
if ($pr > 0) {
$k++;
if ($k == $pr) {
fgets(STDIN);
$k = 0;
}
}
}
}
if ($pr < 0)
fclose($out);
}
}
}
/****************/
/* main program */
/****************/
// 入力ミス
if (count($argv) <= 1)
exit("***error ファイル名を入力して下さい\n");
// 入力OK
else {
// ファイルのオープン
$in = fopen($argv[1], "rb");
if ($in == NULL)
exit("***error ファイル名が不適当です\n");
// 入力データファイル名の入力
fscanf($in, "%d", $n); // データの数
$seed = array($n);
$i_file1 = array($n);
$i_file2 = array($n);
for ($i1 = 0; $i1 < $n; $i1++) {
$seed[$i1] = 1000 * $i1 + 1234567;
fscanf($in, "%s %s", $i_file1[$i1], $i_file2[$i1]);
}
fclose($in);
// 実行(乱数の初期値を変える)
for ($i1 = 0; $i1 < $n; $i1++) {
printf("\n+++++ケース %d+++++\n", $i1+1);
$tsp = new TSP($i_file1[$i1], $i_file2[$i1], $seed[$i1]);
$tsp->Control();
}
}
//----------------ケーススタディデータ(data_ct.txt)------
/*
3
data1_t.txt data2_t.txt
data1_t.txt data2_t.txt
data1_t.txt data2_t.txt
*/
//---------------Species記述データ(data1_t.txt)---------
/*
対立遺伝子上限 9 対立遺伝子下限 0
最大遺伝子長 10 最小遺伝子長(負の時は,最大遺伝子長で固定) -1
遺伝子の重複 0 個体の重複(同じ染色体の個体) 0
集団サイズ 10 子供 10
*/
//---------------TSP記述データ(data2_t.txt)--------
/*
出力レベル(負はファイル) 10 出力方法(0:適応度+順番,1:適応度) 0
出力ファイル名 out1.txt 表示間隔 10
交叉方法 1 交叉確率 1.0 点 5 位置 0 方法 1 バイアス 0 ステップ 1
突然変異方法 1 突然変異率 0.03 幅 1 平均 0.0 標準偏差 1.0
エリート 2 方法 1 バイアス 0 ステップ 1
都市数 10 最大世代交代数 2000
近傍探索(0:行わない,1:行う) 0 近傍(2or3) 2
選択方法(0:最良,1:最初) 1
-58 37
55 -19
6 -79
27 -30
44 -94
33 -58
-94 87
-9 3
33 69
43 -57
*/
?>