巡回セールスマン問題(分割法)

<?php

/****************************/
/* 巡回セールスマン問題     */
/* (分割法)               */
/*      coded by Y.Suganuma */
/****************************/

/*************************/
/* クラスPartitionの定義 */
/*************************/
class Partition {
	private $city;   //都市の位置データ
	private $city_i;   //都市の位置データ(作業領域)
	private $p_x;   // x軸の分割点
	private $p_y;   // y軸の分割点
	private $rg;   // 都市間の距離
	private $seed;   // 乱数の初期値
	private $fix;   // =1 : 近傍を固定
                    // =0 : 近傍を可変
	private $max_try;   // 最大試行回数
	private $n_city;   // 都市の数
	private $n_seq;   // 各領域の都市数
	private $n_seq1;   // 各領域の都市数(ワーク)
	private $n_p_x;   // x軸方向の分割数
	private $n_p_y;   // y軸方向の分割数
	private $seq;   // 経路
	private $seq1;   // 経路(ワーク)
	private $seq_w1;   // 作業領域
	private $seq_w2;   // 作業領域
	private $neib;   // 近傍(2 or 3)
	private $seisu;   // 位置データの表現方法
                      //   =1 : 整数
                      //   =-1 : 実数(距離を整数計算)
                      //   =-2 : 実数(距離を実数計算)
	private $sel;   // エッジの選択方法
                    //   =0 : 最良のものを選択
                    //   =1 : 最初のものを選択
	private $state;   // 領域結合用ワーク
	private $i_file;   // 入力ファイル名

	public $Max;   // 最適経路の長さ
	public $out_m;   // 出力方法
                     //   =-1 : ディスプレイ(経路長だけ)
                     //   =0 : ディスプレイ
                     //   =1 : ファイル
                     //   =2 : ファイル(経路長だけ)
	public $o_file;   // 出力ファイル名

	/**************************/
	/* コンストラクタ         */
	/*      name : ファイル名 */
	/**************************/
	function Partition($name)
	{
		$max = 0;
						// ファイルのオープン
		$this->i_file = $name;
		$in           = fopen($name, "r");
		if ($in == NULL)
			exit("***error  データファイル名が不適当\n");
						// 基本データ
		fscanf($in, "%*s %d %*s %d %*s %d %*s %d", $this->n_city, $this->sel, $this->neib, $this->seisu);
		fscanf($in, "%*s %d %*s %s", $this->out_m, $this->o_file);
    	fscanf($in, "%*s %*s %d %*s %d %*s %d", $this->n_p_x, $this->n_p_y, $this->max_try);
    
    	if ($this->neib < 0) {
			$this->neib = -$this->neib;
    		$this->fix  = 0;
    	}
		else
			$this->fix = 1;
						// 都市の位置データ
		$this->city = array($this->n_city);
		for ($i1 = 0; $i1 < $this->n_city; $i1++) {
    		$this->city[$i1] = array(2);
    		fscanf($in, "%f %f", $this->city[$i1][0], $this->city[$i1][1]);
    	}
    					// ファイルのクローズ
		fclose($in);
						// 距離テーブルの作成
		$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] = sqrt($x * $x + $y * $y);
				if ($this->seisu > -2)
					$this->rg[$i1][$i2] = round($this->rg[$i1][$i2]);
			}
		}
    
		for ($i1 = 1; $i1 < $this->n_city; $i1++) {
			for ($i2 = 0; $i2 < $i1; $i2++)
				$this->rg[$i1][$i2] = $this->rg[$i2][$i1];
		}
						// 作業領域
		$this->state  = array($this->n_p_y);
		$this->n_seq  = array($this->n_p_y);
		$this->n_seq1 = array($this->n_p_y);
		for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
			$this->n_seq[$i1]  = array($this->n_p_x);
			$this->n_seq1[$i1] = array($this->n_p_x);
			$this->state[$i1]  = array($this->n_p_x);
    	}
    
    	$this->seq  = array($this->n_p_y);
		$this->seq1 = array($this->n_p_y);
    	for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
    		$this->seq[$i1]  = array($this->n_p_x);
			$this->seq1[$i1] = array($this->n_p_x);
		}
	
		$this->seq_w1 = array($this->n_city);
		$this->seq_w2 = array($this->n_city);
    	$this->p_x    = array($this->n_p_x);
    	$this->p_y    = array($this->n_p_y);
    					// 都市の分割
    	for ($i1 = 0; $i1 < $this->n_city; $i1++)
			$this->seq_w1[$i1] = 0;
	
		$min_x = $this->city[0][0];
		$max_x = $this->city[0][0];
		$min_y = $this->city[0][1];
		$max_y = $this->city[0][1];
	
		for ($i1 = 1; $i1 < $this->n_city; $i1++) {
			if ($this->city[$i1][0] < $min_x)
				$min_x = $this->city[$i1][0];
			else {
				if ($this->city[$i1][0] > $max_x)
					$max_x = $this->city[$i1][0];
			}
    		if ($this->city[$i1][1] < $min_y)
				$min_y = $this->city[$i1][1];
			else {
				if ($this->city[$i1][1] > $max_y)
					$max_y = $this->city[$i1][1];
			}
		}
	
		$s_x                       = ($max_x - $min_x) / $this->n_p_x;
		$this->p_x[0]              = $min_x + $s_x;
		$this->p_x[$this->n_p_x-1] = $max_x;
		for ($i1 = 1; $i1 < $this->n_p_x-1; $i1++)
			$this->p_x[$i1] = $this->p_x[0] + $i1 * $s_x;
    
    	$s_y                       = ($max_y - $min_y) / $this->n_p_y;
    	$this->p_y[0]              = $min_y + $s_y;
		$this->p_y[$this->n_p_y-1] = $max_y;
    	for ($i1 = 1; $i1 < $this->n_p_y-1; $i1++)
    		$this->p_y[$i1] = $this->p_y[0] + $i1 * $s_y;
	
		for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
			for ($i2 = 0; $i2 < $this->n_p_x; $i2++) {
				$n = 0;
				for ($i3 = 0; $i3 < $this->n_city; $i3++) {
    				if ($this->seq_w1[$i3] == 0) {
    					if ($this->city[$i3][0] <= $this->p_x[$i2] && $this->city[$i3][1] <= $this->p_y[$i1]) {
    						$this->seq_w1[$i3] = 1;
    						$this->seq_w2[$n]  = $i3;
							$n++;
						}
					}
				}
				$this->n_seq1[$i1][$i2] = $n;
				if ($n > 0) {
					$this->seq[$i1][$i2]  = array($this->n_city);
					$this->seq1[$i1][$i2] = array($this->n_city);
					for ($i3 = 0; $i3 < $n; $i3++)
						$this->seq1[$i1][$i2][$i3] = $this->seq_w2[$i3];
					if ($n > $max)
						$max = $n;
				}
			}
    	}
						// 作業領域
		printf("最大都市数 %d\n", $max);
		$this->city_i = array($max);
		for ($i1 = 0; $i1 < $max; $i1++)
			$this->city_i[$i1] = array(2);
	}
	
	/******************************/
    /* 最適化の実行               */
    /*      seed_i : 乱数の初期値 */
    /******************************/
    function Optimize($seed_i)
	{
		$r = 0;
						// 初期設定
		$this->seed = $seed_i;
		mt_srand($seed_i);
						// 分割数と開始時間の出力
		if ($this->out_m > 0)
			$this->Output(0, $r);
	
		for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
			for ($i2 = 0; $i2 < $this->n_p_x; $i2++) {
				$this->n_seq[$i1][$i2] = $this->n_seq1[$i1][$i2];
				for ($i3 = 0; $i3 < $this->n_seq1[$i1][$i2]; $i3++)
    				$this->seq[$i1][$i2][$i3] = $this->seq1[$i1][$i2][$i3];
			}
		}
						// 分割毎の最適化
		for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
			for ($i2 = 0; $i2 < $this->n_p_x; $i2++) {
				if ($this->n_seq[$i1][$i2] > 3) {
							// 近傍の大きさ
					$nb = ($this->n_seq[$i1][$i2] > 3) ? $this->neib : 2;
							// 都市位置データの設定
					for ($i3 = 0; $i3 < $this->n_seq[$i1][$i2]; $i3++) {
						$k                    = $this->seq[$i1][$i2][$i3];
						$this->city_i[$i3][0] = $this->city[$k][0];
    					$this->city_i[$i3][1] = $this->city[$k][1];
    				}
    						// 最適化
					$it  = new Iteration ($this->n_seq[$i1][$i2], $this->max_try, $this->seisu, $this->sel, $nb, $this->fix, 0, -1, 0, $this->o_file, $this->city_i);
    				$max = $it->Optimize();
							// 結果の保存
					for ($i3 = 0; $i3 < $this->n_seq[$i1][$i2]; $i3++) {
						$k                 = $it->seq[$i3];
						$this->seq_w1[$i3] = $this->seq[$i1][$i2][$k];
					}
    				for ($i3 = 0; $i3 < $this->n_seq[$i1][$i2]; $i3++)
    					$this->seq[$i1][$i2][$i3] = $this->seq_w1[$i3];
    						// 出力
    				$r = ($this->seisu > -2) ? intval(kyori($this->n_seq[$i1][$i2], $this->seq[$i1][$i2], $this->rg)) : round((kyori($this->n_seq[$i1][$i2], $this->seq[$i1][$i2], $this->rg)));
					printf("   y %d x %d $this->n_city %d range %d (trial %d)\n",
	                       $i1+1, $i2+1, $this->n_seq[$i1][$i2], $r, $max);
				}
			}
		}
						// 経路の接続
		$r = $this->Connect();
						// 出力
		$this->Output($this->n_city, $r);
	}
	
	/***********************/
	/* 出力                */
    /*      n_c : 都市の数 */
	/*      r : 距離       */
	/***********************/
	function Output($n_c, $r)
	{
		$k = 0;
	
		if ($this->out_m <= 0) {
			$out = STDOUT;
			fwrite($out, "距離 ".$r."\n");
    		fgets(STDIN);
    	}
    	else {
			$x   = getdate();
			$now = $x["hours"]."時".$x["minutes"]."分".$x["seconds"]."秒";
    		$out = fopen($this->o_file, "ab");
			if ($n_c > 0) {
				printf("距離 %d\n", $r);
				fwrite($out, "   距離 ".$r." 時間 ".$now."\n");
			}
			else
    			fwrite($out, "問題 ".$this->i_file." 乱数 ".$this->seed." 分割 ".$this->n_p_x." ".$this->n_p_y." 時間 ".$now."\n");
		}
    
		if ($n_c > 0 && ($this->out_m == 0 || $this->out_m == 1)) {
			for ($i1 = 0; $i1 < $n_c; $i1++) {
				$n = $this->seq_w1[$i1];
				if ($this->seisu > 0)
					fwrite($out, "  ".$n." ".intval($this->city[$n][0])." ".intval($this->city[$n][1])."\n");
				else
					fwrite($out, "  ".$n." ".$this->city[$n][0]." ".$this->city[$n][1]."\n");
				if ($this->out_m == 0) {
					$k++;
					if ($k == 10) {
						fgets(STDIN);
						$k = 0;
					}
				}
    		}
		}
	
		if ($this->out_m > 0)
			fclose($out);
	}
	
	/************************/
	/* 分割された領域の接続 */
	/************************/
	function Connect()
	{
		$min   = 0;
    	$k1    = 0;
		$k2    = 0;
		$k3    = 0;
		$k4    = 0;
		$min_c = 0;
		$r1    = 0;
		$r2    = 0;
		$r3    = 0;
		$r4    = 0;
		$s1    = 0;
		$s2    = 0;
		$sw    = 1;
    /*
	     領域が1つの場合
    */
    	if ($this->n_p_x == 1 && $this->n_p_y == 1) {
			for ($i1 = 0; $i1 < $this->n_seq[0][0]; $i1++)
				$this->seq_w1[$i1] = $this->seq[0][0][$i1];
		}
	/*
	     初期設定
    */
    	else {
    
    		for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
				for ($i2 = 0; $i2 < $this->n_p_x; $i2++)
					$this->state[$i1][$i2] = ($this->n_seq[$i1][$i2] > 0) ? 0 : 1;
			}
	/*
	     実行
	*/
			while ($sw > 0) {
						// 最小節点領域
				$min_c = $this->n_city;
				$sw    = 0;
				for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
					for ($i2 = 0; $i2 < $this->n_p_x; $i2++) {
						if ($this->state[$i1][$i2] == 0 && $this->n_seq[$i1][$i2] < $min_c) {
							$sw    = 1;
    						$r1    = $i1;
							$r2    = $i2;
							$min_c = $this->n_seq[$i1][$i2];
						}
					}
				}
						// 結合する対象領域の決定
				if ($sw > 0) {
					$sw = 0;
					for ($i1 = 0; $i1 < $this->n_p_y; $i1++) {
						for ($i2 = 0; $i2 < $this->n_p_x; $i2++) {
							if ($this->state[$i1][$i2] == 0 && ($i1 != $r1 || $i2 != $r2)) {
									// 節点の数>2
    							if ($this->n_seq[$r1][$r2] > 1) {
    								for ($i3 = 0; $i3 < $this->n_seq[$r1][$r2]; $i3++) {
    									$k1  = $this->seq[$r1][$r2][$i3];
										$k2  = ($i3 == $this->n_seq[$r1][$r2]-1) ? $this->seq[$r1][$r2][0] : $this->seq[$r1][$r2][$i3+1];
    									$wd1 = $this->rg[$k1][$k2];
										for ($i4 = 0; $i4 < $this->n_seq[$i1][$i2]; $i4++) {
											$k3  = $this->seq[$i1][$i2][$i4];
											$k4  = ($i4 == $this->n_seq[$i1][$i2]-1) ? $this->seq[$i1][$i2][0] : $this->seq[$i1][$i2][$i4+1];
											$wd  = $wd1 + $this->rg[$k3][$k4];
    										$wa1 = $this->rg[$k1][$k3] + $this->rg[$k2][$k4];
    										$wa2 = $this->rg[$k1][$k4] + $this->rg[$k2][$k3];
    										if ($sw == 0 || $wa1-$wd < $min) {
    											$min = $wa1 - $wd;
												$r3  = $i1;
												$r4  = $i2;
												$s1  = ($i3 == $this->n_seq[$r1][$r2]-1) ? 0 : $i3 + 1;
												$s2  = ($i4 == $this->n_seq[$i1][$i2]-1) ? 0 : $i4 + 1;
												$sw  = -1;
											}
											if ($sw == 0 || $wa2-$wd < $min) {
												$min = $wa2 - $wd;
												$r3  = $i1;
												$r4  = $i2;
												$s1  = $i3;
												$s2  = ($i4 == $this->n_seq[$i1][$i2]-1) ? 0 : $i4 + 1;
												$sw  = 1;
											}
    									}
									}
								}
									// 節点の数=1
								else {
									$k1 = $this->seq[$r1][$r2][0];
									if ($this->n_seq[$i1][$i2] > 1) {
										for ($i4 = 0; $i4 < $this->n_seq[$i1][$i2]; $i4++) {
											$k3  = $this->seq[$i1][$i2][$i4];
											$k4  = ($i4 == $this->n_seq[$i1][$i2]-1) ? $this->seq[$i1][$i2][0] : $this->seq[$i1][$i2][$i4+1];
											$wd  = $this->rg[$k3][$k4];
											$wa1 = $this->rg[$k1][$k3] + $this->rg[$k1][$k4];
    										if ($sw == 0 || $wa1-$wd < $min) {
    											$min = $wa1 - $wd;
    											$r3  = $i1;
												$r4  = $i2;
    											$s1  = 0;
    											$s2  = ($i4 == $this->n_seq[$i1][$i2]-1) ? 0 : $i4 + 1;
												$sw  = 1;
											}
										}
									}
									else {
    									$k3  = $this->seq[$i1][$i2][0];
    									$wa1 = $this->rg[$k1][$k3];
    									if ($sw == 0 || $wa1 < $min) {
    										$min = $wa1;
											$r3  = $i1;
											$r4  = $i2;
											$s1  = 0;
											$s2  = 0;
											$sw  = 1;
										}
									}
								}
							}
						}
					}
						// 領域の結合
					$this->seq_w1[0] = $this->seq[$r1][$r2][$s1];
					$k               = 1;
    				$n               = $s2;
					for ($i1 = 0; $i1 < $this->n_seq[$r3][$r4]; $i1++) {
						$this->seq_w1[$k] = $this->seq[$r3][$r4][$n];
						$k++;
						$n++;
						if ($n > $this->n_seq[$r3][$r4]-1)
							$n = 0;
					}
					if ($sw > 0) {
						$n = $s1 + 1;
						for ($i1 = 0; $i1 < $this->n_seq[$r1][$r2]-1; $i1++) {
							if ($n > $this->n_seq[$r1][$r2]-1)
								$n = 0;
    						$this->seq_w1[$k] = $this->seq[$r1][$r2][$n];
    						$k++;
    						$n++;
						}
    				}
    				else {
						$n = $s1 - 1;
						for ($i1 = 0; $i1 < $this->n_seq[$r1][$r2]-1; $i1++) {
							if ($n < 0)
								$n = $this->n_seq[$r1][$r2] - 1;
							$this->seq_w1[$k] = $this->seq[$r1][$r2][$n];
    						$k++;
    						$n--;
    					}
    				}
						// 状態の変更
					$this->n_seq[$r1][$r2] += $this->n_seq[$r3][$r4];
					$this->state[$r3][$r4]  = 1;
					for ($i1 = 0; $i1 < $this->n_seq[$r1][$r2]; $i1++)
						$this->seq[$r1][$r2][$i1] = $this->seq_w1[$i1];
					$sw  = 1;
				}
			}
		}

		$r         = ($this->seisu > -2) ? intval(kyori($this->n_city, $this->seq_w1, $this->rg)) : round(kyori($this->n_city, $this->seq_w1, $this->rg));
		$this->Max = $r;

		return $r;
	}
}

/*************************/
/* クラスIterationの定義 */
/*************************/
class Iteration {
	private $city;   //都市の位置データ
	private $rg;   // 都市間の距離
	private $fix;   // =1 : 近傍を固定
                    // =0 : 近傍を可変
	private $max_try;   // 最大試行回数
	private $n_city;   // 都市の数
	private $out_d;   // 表示間隔
	private $seq_w1;   // 都市を訪れる順序(ワーク)
	private $seq_w2;   // 都市を訪れる順序(ワーク)
	private $seq_w3;   // 都市を訪れる順序(ワーク)
	private $seq_w4;   // 都市を訪れる順序(ワーク)
	private $seq_w5;   // 都市を訪れる順序(ワーク)
	private $neib;   // 近傍(2 or 3)
	private $out_lvl;   // 出力レベル
                        //   =0 : 最終出力だけ
                        //   n>0 : n世代毎に出力(負の時はファイル)
	private $out_m;   // 出力方法
                      //   =-1 : 出力しない
                      //   =0 : すべてを出力
                      //   =1 : 評価値だけを出力(最終結果だけはすべてを出力)
	private $seisu;   // 位置データの表現方法
                      //   =1 : 整数
                      //   =-1 : 実数(距離を整数計算)
                      //   =-2 : 実数(距離を実数計算)
	private $sel;   // エッジの選択方法
                    //   =0 : 最良のものを選択
                    //   =1 : 最初のものを選択
	private $o_file;   // 出力ファイル名

	public $seq;   // 都市を訪れる順序

	/**********************************/
	/* コンストラクタ                 */
	/*      n_city_i : 都市の数       */
    /*      max_try_i : 最大試行回数  */
	/*      sei_i : 整数 or 実数      */
	/*      sel_i : エッジの選択方法  */
	/*      neib_i : 近傍             */
	/*      fix_i : 近傍の扱い方      */
	/*      out_lvl_i : 出力レベル    */
	/*      out_m_i : 出力方法        */
	/*      out_d_i : 表示間隔        */
	/*      o_file_i : 出力ファイル名 */
	/*      city_i : 都市の位置データ */
	/**********************************/
    function Iteration ($n_city_i, $max_tri_i, $sei_i, $sel_i, $neib_i, $fix_i, $out_lvl_i, $out_m_i, $out_d_i, $o_file_i, $city_i)
	{
    					// 値の設定
		$this->n_city  = $n_city_i;
    	$this->max_try = $max_tri_i;
    	$this->seisu   = $sei_i;
    	$this->sel     = $sel_i;
		$this->neib    = $neib_i;
    	$this->fix     = $fix_i;
    	$this->out_lvl = $out_lvl_i;
		$this->out_m   = $out_m_i;
		$this->out_d   = $out_d_i;
		$this->o_file  = $o_file_i;
						// 都市の位置データ
		$this->city = array($this->n_city);
		for ($i1 = 0; $i1 < $this->n_city; $i1++) {
			$this->city[$i1]    = array(2);
			$this->city[$i1][0] = $city_i[$i1][0];
			$this->city[$i1][1] = $city_i[$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] = sqrt($x * $x + $y * $y);
				if ($this->seisu > -2)
					$this->rg[$i1][$i2] = round($this->rg[$i1][$i2]);
    		}
    	}
	
    	for ($i1 = 1; $i1 < $this->n_city; $i1++) {
    		for ($i2 = 0; $i2 < $i1; $i2++)
    			$this->rg[$i1][$i2] = $this->rg[$i2][$i1];
		}
    					// 都市を訪れる順序(初期設定)
    	$this->seq    = array($this->n_city);
    	$this->seq_w1 = array($this->n_city);
		$this->seq_w2 = array($this->n_city);
    	$this->seq_w3 = array($this->n_city);
    	$this->seq_w4 = array($this->n_city);
		$this->seq_w5 = array($this->n_city);
	
		for ($i1 = 0; $i1 < $this->n_city; $i1++) {
			$sw = 0;
			while ($sw == 0) {
				$ct = intval((mt_rand() / mt_getrandmax()) * $this->n_city);
				if ($ct >= $this->n_city)
					$ct = $this->n_city - 1;
				$this->seq[$i1] = $ct;
				$sw      = 1;
				for ($i2 = 0; $i2 < $i1 && $sw > 0; $i2++) {
    				if ($ct == $this->seq[$i2])
						$sw = 0;
				}
			}
		}
	}
	
	/****************/
	/* 最適化の実行 */
	/****************/
    function Optimize()
	{
						// 初期設定
		$n_tri = 0;
		$max   = kyori($this->n_city, $this->seq, $this->rg);
	
		if ($this->out_m >= 0 && abs($this->out_lvl) > 0) {
			if ($this->seisu > -2)
				printf("***試行回数 %d 距離 %d\n", $n_tri, intval($max));
    		else
    			printf("***試行回数 %d 距離 %f\n", $n_tri, $max);
			$this->Output($this->out_lvl, $n_tri, $max);
    	}
    					// 実行
    	$sw = 1;
		for ($n_tri = 1; $n_tri <= $this->max_try && $sw > 0; $n_tri++) {
    						// 改善
    		$sw = $this->Change($max);
    						// 出力
			if ($this->out_d > 0 && $n_tri%$this->out_d == 0) {
    			if ($this->seisu > -2)
    				printf("***試行回数 %d 距離 %d\n", $n_tri, intval($max));
				else
					printf("***試行回数 %d 距離 %f\n", $n_tri, $max);
			}
	
			if ($this->out_m >= 0 && abs($this->out_lvl) > 0) {
				if ($n_tri%abs($this->out_lvl) == 0)
					$this->Output($this->out_lvl, $n_tri, $max);
			}
		}
						// 最終出力
		if ($this->out_m >= 0) {
    		$n_tri--;
			if ($this->seisu > -2)
				printf("***試行回数 %d 距離 %d\n", $n_tri, intval($max));
			else
				printf("***試行回数 %d 距離 %f\n", $n_tri, $max);
			$this->Output($this->out_lvl, $n_tri, $max);
		}
	
		return $n_tri;
	}
	
    /*******************************/
    /* 出力                        */
	/*      sw : >=0 : 出力先未定  */
    /*           < 0 : ファイル    */
    /*      n_tri : 現在の試行回数 */
    /*      r : 距離               */
	/*******************************/
    function Output($sw, $n_tri, $r)
    {
    	$k = 0;
	
		if ($sw >= 0) {
			printf("   出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ");
			scanf(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");
				if ($this->seisu > -2)
					fwrite($out, "***試行回数 ".$n_tri." 距離 ".intval($r)." 時間 ".$now."\n");
				else
					fwrite($out, "***試行回数 ".$n_tri." 距離 ".round($r)." 時間 ".$now."\n");
    		}
    
			if ($this->out_m == 0) {
    			for ($i1 = 0; $i1 < $this->n_city; $i1++) {
    				$n = $this->seq[$i1];
    				if ($this->seisu > 0)
						fwrite($out, "  ".$n." ".intval($this->city[$n][0])." ".intval($this->city[$n][1])."\n");
    				else
    					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);
		}
	}
	
    /**************************************/
	/* エッジの入れ替え                   */
	/*      r_m : 距離                    */
	/*      return : =0 : 改善がなかった  */
	/*               =1 : 改善があった    */
	/**************************************/
	function Change(&$r_m)
	{
		$max1 = 0.0;
		$ch   = 0;
		$k1   = 0;
		$k2   = 0;
		$n1   = 0;
		$n2   = 0;
		$sw   = 0;
		$sw1  = 0;
    
    	$max  = $r_m;
	
    /*
         近傍を可変
    */
		if ($this->fix == 0) {
    					// 初期設定(k=2)
    		$k = 2;
    		for ($i1 = 0; $i1 < $this->n_city; $i1++) {
				$this->seq_w4[$i1] = $this->seq[$i1];
    			$this->seq_w3[$i1] = 0;
    		}
							// 評価
			$sw2 = 0;
			for ($i0 = 0; $i0 < $this->n_city-2 && $sw2 < 2; $i0++) {
	
				$n = ($i0 == 0) ? $this->n_city-1 : $this->n_city;
	
				for ($i1 = $i0+2; $i1 < $n && $sw2 < 2; $i1++) {
								// 相手の場所
					$k3 = $i1;
					$k4 = $k3 + 1;
					if ($k4 > $this->n_city-1)
    					$k4 = 0;
								// 順番の入れ替え
					$n3 = -1;
					for ($i2 = 0; $i2 < $this->n_city && $n3 < 0; $i2++) {
						if ($this->seq_w4[$i2] == $this->seq[$i0+1])
							$n3 = $i2 + 1;
					}
					$nn = $n3;
					$n4 = -1;
					for ($i2 = 0; $i2 < $this->n_city && $n4 < 0; $i2++) {
						if ($nn > $this->n_city-1)
    						$nn = 0;
    					if ($this->seq_w4[$nn] == $this->seq[$k3] || $this->seq_w4[$nn] == $this->seq[$k4])
							$n4 = $this->seq_w4[$nn];
    					else
    						$nn++;
    				}
					if ($n4 == $this->seq[$k4]) {
    					$n4 = $k3;
    					$k3 = $k4;
    					$k4 = $n4;
					}
    							// 評価
    				$this->seq_w1[0] = $this->seq[$k4];
					$this->seq_w1[1] = $this->seq[$i0+1];
					$n4              = -1;
					$nn              = 2;
					while ($n4 < 0) {
						if ($n3 > $this->n_city-1)
							$n3 = 0;
						$this->seq_w1[$nn] = $this->seq_w4[$n3];
						if ($this->seq_w4[$n3] == $this->seq[$k3])
							$n4 = 1;
						$nn++;
						$n3++;
    				}
					$this->seq_w1[$nn] = $this->seq[$i0];
					$nn++;
					$n3 = -1;
					$n4 = -1;
					for ($i2 = 0; $i2 < $this->n_city && $n3 < 0; $i2++) {
						if ($this->seq_w4[$i2] == $this->seq[$i0]) {
							$n3 = $i2 - 1;
							if ($n3 < 0)
								$n3 = $this->n_city - 1;
						}
    				}
    				while ($n4 < 0) {
						if ($this->seq_w4[$n3] == $this->seq[$k4])
    						$n4 = 1;
    					else {
    						$this->seq_w1[$nn] = $this->seq_w4[$n3];
							$nn++;
    						$n3--;
    						if ($n3 < 0)
    							$n3 = $this->n_city - 1;
						}
    				}
    
					$r = kyori($this->n_city, $this->seq_w1, $this->rg);
								// 最適値の保存
					if ($sw2 == 0 || $r < $max1) {
						$sw2  = 1;
						$max1 = $r;
						$n1   = $k3;
						$n2   = $k4;
						$k1   = $i0;
						$k2   = $i0 + 1;
						for ($i2 = 0; $i2 < $this->n_city; $i2++)
							$this->seq_w5[$i2] = $this->seq_w1[$i2];
    					if ($this->sel > 0 && $max1 < $max)
							$sw2 = 2;
					}
				}
			}
							// 最適値の保存と近傍の増加
			if ($sw2 > 0) {
				if ($max1 < $max) {
					$sw  = 1;
					$max = $max1;
					for ($i1 = 0; $i1 < $this->n_city; $i1++)
    					$this->seq_w2[$i1] = $this->seq_w5[$i1];
    			}
				if ($k < $this->neib) {
    				for ($i1 = 0; $i1 < $this->n_city; $i1++)
    					$this->seq_w4[$i1] = $this->seq_w5[$i1];
    				$this->seq_w3[$k1] = 1;
					$this->seq_w3[$k2] = 1;
    				$this->seq_w3[$n1] = 1;
    				$this->seq_w3[$n2] = 1;
    				$k1                = $n2;
					$k++;
    			}
    			else
					$sw1 = 1;
			}
			else
				$sw1 = 1;
						// 実行(k>2)
			while ($sw1 == 0) {
							// 評価
				$sw2 = 0;
				for ($i1 = 0; $i1 < $this->n_city; $i1++) {
								// 相手の場所
					$k3 = $i1;
    				$k4 = $k3 + 1;
					if ($k4 > $this->n_city-1)
						$k4 = 0;
	
					if ($this->seq_w3[$k3] == 0 && $this->seq_w3[$k4] == 0) {
								// 順番の入れ替え
						$n3 = -1;
						for ($i2 = 0; $i2 < $this->n_city && $n3 < 0; $i2++) {
							if ($this->seq_w4[$i2] == $this->seq[$k2])
								$n3 = $i2 + 1;
						}
    					$nn = $n3;
    					$n4 = -1;
						for ($i2 = 0; $i2 < $this->n_city && $n4 < 0; $i2++) {
    						if ($nn > $this->n_city-1)
    							$nn = 0;
    						if ($this->seq_w4[$nn] == $this->seq[$k3] || $this->seq_w4[$nn] == $this->seq[$k4])
								$n4 = $this->seq_w4[$nn];
    						else
    							$nn++;
    					}
						if ($n4 == $this->seq[$k4]) {
    						$n4 = $k3;
    						$k3 = $k4;
							$k4 = $n4;
						}
								// 評価
						$this->seq_w1[0] = $this->seq[$k4];
						$this->seq_w1[1] = $this->seq[$k2];
						$n4              = -1;
						$nn              = 2;
						while ($n4 < 0) {
							if ($n3 > $this->n_city-1)
								$n3 = 0;
							$this->seq_w1[$nn] = $this->seq_w4[$n3];
    						if ($this->seq_w4[$n3] == $this->seq[$k3])
								$n4 = 1;
							$nn++;
							$n3++;
						}
						$this->seq_w1[$nn] = $this->seq[$k1];
						$nn++;
						$n3 = -1;
						$n4 = -1;
						for ($i2 = 0; $i2 < $this->n_city && $n3 < 0; $i2++) {
							if ($this->seq_w4[$i2] == $this->seq[$k1]) {
    							$n3 = $i2 - 1;
    							if ($n3 < 0)
									$n3 = $this->n_city - 1;
    						}
    					}
    					while ($n4 < 0) {
							if ($this->seq_w4[$n3] == $this->seq[$k4])
    							$n4 = 1;
    						else {
    							$this->seq_w1[$nn] = $this->seq_w4[$n3];
								$nn++;
    							$n3--;
    							if ($n3 < 0)
									$n3 = $this->n_city - 1;
							}
						}
	
						$r = kyori($this->n_city, $this->seq_w1, $this->rg);
								// 最適値の保存
						if ($sw2 == 0 || $r < $max1) {
							$sw2  = 1;
							$max1 = $r;
							$n1   = $k3;
							$n2   = $k4;
    						for ($i2 = 0; $i2 < $this->n_city; $i2++)
								$this->seq_w5[$i2] = $this->seq_w1[$i2];
						}
					}
				}
							// 最適値の保存と近傍の増加
				if ($sw2 > 0) {
					if ($max1 < $max) {
						$sw  = 1;
						$max = $max1;
						for ($i1 = 0; $i1 < $this->n_city; $i1++)
    						$this->seq_w2[$i1] = $this->seq_w5[$i1];
    				}
					if ($k < $this->neib) {
    					for ($i1 = 0; $i1 < $this->n_city; $i1++)
    						$this->seq_w4[$i1] = $this->seq_w5[$i1];
    					$this->seq_w3[$n1] = 1;
						$this->seq_w3[$n2] = 1;
    					$k1         = $n2;
    					$k++;
    				}
					else
    					$sw1 = 1;
    			}
				else
					$sw1 = 1;
			}
		}
	/*
	     近傍を固定
	*/
		else {
			$n3  = intval((mt_rand() / mt_getrandmax()) * ($this->n_city - 2));
			if ($n3 > $this->n_city-3)
				$n3 = $this->n_city - 3;
                             // 2近傍
			for ($i1 = 0; $i1 <= $this->n_city-3 && $ch == 0; $i1++) {
	
				if ($n3 == 0)
					$n1 = $this->n_city - 2;
				else
					$n1 = $this->n_city - 1;
	
				for ($i2 = $n3+2; $i2 <= $n1 && $ch == 0; $i2++) {
	                              // 枝の場所((n3,n3+1), (k1,k2))
					$k1 = $i2;
    				if ($i2 == $this->n_city-1)
    					$k2 = 0;
					else
    					$k2 = $i2 + 1;
                                  // 枝の入れ替え
    				$this->seq_w1[0] = $this->seq[$n3];
					$k         = 1;
    				for ($i3 = $k1; $i3 >= $n3+1; $i3--) {
    					$this->seq_w1[$k] = $this->seq[$i3];
    					$k++;
					}
    
    				$nn = $k2;
					while ($nn != $n3) {
						$this->seq_w1[$k] = $this->seq[$nn];
						$k++;
						$nn++;
						if ($nn > $this->n_city-1)
							$nn = 0;
					}
	                              // 評価
					$r = kyori($this->n_city, $this->seq_w1, $this->rg);
	
					if ($r < $max) {
    					$max = $r;
						$sw  = 1;
						for ($i3 = 0; $i3 < $this->n_city; $i3++)
							$this->seq_w2[$i3] = $this->seq_w1[$i3];
						if ($this->sel > 0)
							$ch = 1;
					}
				}
	
				$n3++;
				if ($n3 > $this->n_city-3)
    				$n3 = 0;
    		}
	                         // 3近傍
    		if ($this->neib == 3 && $ch == 0) {
    
    			for ($i1 = 0; $i1 <= $this->n_city-3 && $ch == 0; $i1++) {
	
    				$n1 = $this->n_city - 2;
    				$n2 = $this->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 == $this->n_city-1)
								$k2 = 0;
							else
								$k2 = $i3 + 1;
	                              // 枝の入れ替えと評価
	                                   // 入れ替え(その1)
							$this->seq_w1[0] = $this->seq[$n3];
							$k         = 1;
							for ($i4 = $i2; $i4 >= $n3+1; $i4--) {
    							$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
	
							for ($i4 = $k1; $i4 >= $i2+1; $i4--) {
								$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
	
							$nn = $k2;
							while ($nn != $n3) {
    							$this->seq_w1[$k] = $this->seq[$nn];
    							$k++;
								$nn++;
    							if ($nn > $this->n_city-1)
    								$nn = 0;
    						}
	                                   // 評価(その1)
    						$r = kyori($this->n_city, $this->seq_w1, $this->rg);
    
    						if ($r < $max) {
								$max = $r;
    							$sw  = 1;
    							for ($i3 = 0; $i3 < $this->n_city; $i3++)
									$this->seq_w2[$i3] = $this->seq_w1[$i3];
								if ($this->sel > 0)
									$ch = 1;
							}
	                                   // 入れ替え(その2)
							$this->seq_w1[0] = $this->seq[$n3];
							$k         = 1;
							for ($i4 = $k1; $i4 >= $i2+1; $i4--) {
								$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
    
							for ($i4 = $n3+1; $i4 <= $i2; $i4++) {
								$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
	
							$nn = $k2;
							while ($nn != $n3) {
								$this->seq_w1[$k] = $this->seq[$nn];
								$k++;
								$nn++;
    							if ($nn > $this->n_city-1)
    								$nn = 0;
							}
                                       // 評価(その2)
    						$r = kyori($this->n_city, $this->seq_w1, $this->rg);
    
							if ($r < $max) {
    							$max = $r;
    							$sw  = 1;
    							for ($i3 = 0; $i3 < $this->n_city; $i3++)
									$this->seq_w2[$i3] = $this->seq_w1[$i3];
    							if ($this->sel > 0)
    								$ch = 1;
							}
	                                   // 入れ替え(その3)
							$this->seq_w1[0] = $this->seq[$n3];
							$k         = 1;
							for ($i4 = $i2+1; $i4 <= $k1; $i4++) {
								$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
	
							for ($i4 = $i2; $i4 >= $n3+1; $i4--) {
								$this->seq_w1[$k] = $this->seq[$i4];
    							$k++;
							}
	
							$nn = $k2;
							while ($nn != $n3) {
								$this->seq_w1[$k] = $this->seq[$nn];
								$k++;
								$nn++;
								if ($nn > $this->n_city-1)
									$nn = 0;
							}
                                       // 評価(その3)
    						$r = kyori($this->n_city, $this->seq_w1, $this->rg);
	
    						if ($r < $max) {
    							$max = $r;
    							$sw  = 1;
								for ($i3 = 0; $i3 < $this->n_city; $i3++)
    								$this->seq_w2[$i3] = $this->seq_w1[$i3];
    							if ($this->sel > 0)
    								$ch = 1;
							}
                                       // 入れ替え(その4)
    						$this->seq_w1[0] = $this->seq[$n3];
							$k               = 1;
							for ($i4 = $i2+1; $i4 <= $k1; $i4++) {
								$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
	
							for ($i4 = $n3+1; $i4 <= $i2; $i4++) {
								$this->seq_w1[$k] = $this->seq[$i4];
								$k++;
							}
	
    						$nn = $k2;
							while ($nn != $n3) {
								$this->seq_w1[$k] = $this->seq[$nn];
								$k++;
								$nn++;
								if ($nn > $this->n_city-1)
									$nn = 0;
							}
	                                   // 評価(その4)
							$r = kyori($this->n_city, $this->seq_w1, $this->rg);
	
    						if ($r < $max) {
    							$max = $r;
								$sw  = 1;
    							for ($i3 = 0; $i3 < $this->n_city; $i3++)
    								$this->seq_w2[$i3] = $this->seq_w1[$i3];
    							if ($this->sel > 0)
									$ch = 1;
    						}
    					}
    				}
	
    				$n3++;
    				if ($n3 > $this->n_city-3)
						$n3 = 0;
				}
			}
		}
	                         // 設定
		if ($sw > 0) {
			$r_m = $max;
			for ($i1 = 0; $i1 < $this->n_city; $i1++)
				$this->seq[$i1] = $this->seq_w2[$i1];
		}
	
		return $sw;
	}
}

/*********************************/
/* 距離の計算                    */
/*      n_c : 都市の数           */
/*      p : 都市番号             */
/*      rg : 都市間の距離        */
/*      return : 距離            */
/*********************************/
function kyori($n_c, $p, $rg)
{
	$range = 0;
	$n1    = $p[0];

	for ($i1 = 1; $i1 < $n_c; $i1++) {
		$n2     = $p[$i1];
		$range += $rg[$n1][$n2];
		$n1     = $n2;
	}

	$n2     = $p[0];
	$range += $rg[$n1][$n2];

	return $range;
}

/****************/
/* main program */
/****************/
					// 入力ミス
	if (count($argv) <= 1)
		exit("***error  ファイル名を入力して下さい\n");
					// 入力OK
	else {
						// ファイルのオープン
		$in = fopen($argv[1], "rb");
		if ($in == NULL)
			exit("***error  ファイル名が不適当です\n");
						// 入力データファイル名と問題数
		fscanf($in, "%*s %d", $nm);

		for ($i0 = 0; $i0 < $nm; $i0++) {
							// 各問題の実行
			fscanf($in, "%*s %s %*s %d", $i_file, $n);
			$pt   = new Partition($i_file);
			$mean = 0.0;
			$max  = -1;
								// 乱数の初期値を変える
			for ($i1 = 0; $i1 < $n; $i1++) {
									// 問題
				printf("\n+++++問題 %s +++++\n", $i_file);
									// 最適化
				$pt->Optimize(1000 * $i1 + 1234567);   // 引数は乱数の初期値
									// 最適値とその平均の計算
				$mean += $pt->Max;
				if ($max < 0 || $pt->Max < $max)
					$max = $pt->Max;
			}
							// 結果
			if ($pt->out_m <= 0)
				printf("     -----最小 %d 平均 %f-----\n", $max, $mean/$n);
			else {
				$out = fopen($pt->o_file, "ab");
				$str = sprintf("     -----最小 %d 平均 %f-----\n", $max, $mean/$n);
				fwrite($out, $str);
				fclose($out);
			}
		}

		fclose($in);
	}

//------------------------ケーススタディデータ(data.txt)------
/*
問題の数 2
問題 data1.txt 繰り返し回数 2
問題 data2.txt 繰り返し回数 1
*/
//---------------------データファイル(data1.txt)------------
/*
都市の数 50 選択方法(0:最良,1:最初) 1 近傍(2or3) 2 整数 -2
出力(0:ディスプレイ,1:ファイル) -1 出力ファイル名 out1.txt
分割数 X 2 Y 2 最大試行回数 1000
86.950684 27.711487
82.357788 16.148376
29.791260 37.959290
27.493286 1.542664
90.893555 88.734436
40.109253 92.308044
87.445068 53.474426
24.893188 99.382019
11.633301 80.616760
61.532593 8.702087
30.645752 93.598938
4.714966 81.205750
86.669922 90.858459
84.127808 52.830505
96.893311 45.832825
4.458618 34.513855
53.503418 6.959534
45.394897 12.193298
23.687744 97.676086
61.624146 46.806335
49.633789 16.419983
82.833862 74.290466
48.529053 36.628723
13.711548 5.583191
12.561035 6.739807
33.944702 26.622009
8.917236 50.190735
98.220825 98.344421
79.785156 65.419006
36.227417 56.687927
42.352295 25.862122
52.651978 12.590027
88.806152 79.957581
27.182007 51.988220
86.334229 51.142883
14.505005 35.820007
77.124023 37.855530
44.308472 0.022888
78.363037 13.533020
21.279907 55.534363
82.238770 26.612854
25.106812 88.291931
55.938721 0.532532
10.476685 59.233093
41.650391 33.729553
7.077026 4.295349
56.561279 99.641418
19.595337 34.416199
92.858887 46.705627
27.719116 35.533142
*/

//---------------------データファイル(data2.txt)------------
/*
都市の数 10 選択方法(0:最良,1:最初) 1 近傍(2or3) 2 整数 -2
出力(0:ディスプレイ,1:ファイル) -1 出力ファイル名 out1.txt
分割数 X 1 Y 1 最大試行回数 1000
8.695068 2.771149
8.235779 1.614838
2.979126 3.795929
2.749329 0.154266
9.089355 8.873444
4.010925 9.230804
8.744507 5.347443
2.489319 9.938202
1.163330 8.061676
6.153259 0.870209
*/

?>