<?php /****************************/ /* 線形計画法 */ /* coded by Y.Suganuma */ /****************************/ #include/*************/ /* クラス LP */ /*************/ class LP { private $n; // 制約条件の数 private $m; // 変数の数 private $m_s; // スラック変数の数 private $m_a; // 人為変数の数 private $mm; // m + m_s + m_a private $a = array(); // 人為変数があるか否か private $cp = array(); // 比較演算子(-1: 左辺 < 右辺, 0: 左辺 = 右辺, 1: 左辺 > 右辺) private $row = array(); // 各行の基底変数の番号 private $z = array(); // 目的関数の係数 private $s = array(); // 単体表 private $eps; // 許容誤差 private $err; // エラーコード (0:正常終了, 1:解無し) /******************************/ /* コンストラクタ */ /* n1 : 制約条件の数 */ /* m1 : 変数の数 */ /* z1 : 目的関数の係数 */ /* eq_l : 制約条件の左辺 */ /* eq_r : 制約条件の右辺 */ /* cp1 : 比較演算子 */ /******************************/ function LP($n1, $m1, $z1, $eq_l, $eq_r, $cp1) { // 初期設定 $this->eps = 1.0e-10; $this->err = 0; $this->n = $n1; $this->m = $m1; for ($i1 = 0; $i1 < $this->n; $i1++) { $this->a[$i1] = 0; $this->cp[$i1] = $cp1[$i1]; } for ($i1 = 0; $i1 < $this->m; $i1++) $this->z[$i1] = $z1[$i1]; // スラック変数と人為変数の数を数える $this->m_s = 0; $this->m_a = 0; for ($i1 = 0; $i1 < $this->n; $i1++) { if ($this->cp[$i1] == 0) { $this->m_a++; if ($eq_r[$i1] < 0.0) { $eq_r[$i1] = -$eq_r[$i1]; for ($i2 = 0; $i2 < $this->m; $i2++) $eq_l[$i1][$i2] = -$eq_l[$i1][$i2]; } } else { $this->m_s++; if ($eq_r[$i1] < 0.0) { $this->cp[$i1] = -$this->cp[$i1]; $eq_r[$i1] = -$eq_r[$i1]; for ($i2 = 0; $i2 < $this->m; $i2++) $eq_l[$i1][$i2] = -$eq_l[$i1][$i2]; } if ($this->cp[$i1] > 0) $this->m_a++; } } // 単体表の作成 // 初期設定 $this->mm = $this->m + $this->m_s + $this->m_a; for ($i1 = 0; $i1 <= $this->n; $i1++) { $this->s[$i1] = array($this->mm+1); if ($i1 < $this->n) { $this->s[$i1][0] = $eq_r[$i1]; for ($i2 = 0; $i2 < $this->m; $i2++) $this->s[$i1][$i2+1] = $eq_l[$i1][$i2]; for ($i2 = $this->m+1; $i2 <= $this->mm; $i2++) $this->s[$i1][$i2] = 0.0; } else { for ($i2 = 0; $i2 <= $this->mm; $i2++) $this->s[$i1][$i2] = 0.0; } } // スラック変数 $k = $this->m + 1; for ($i1 = 0; $i1 < $this->n; $i1++) { if ($this->cp[$i1] != 0) { if ($this->cp[$i1] < 0) { $this->s[$i1][$k] = 1.0; $this->row[$i1] = $k - 1; } else $this->s[$i1][$k] = -1.0; $k++; } } // 人為変数 for ($i1 = 0; $i1 < $this->n; $i1++) { if ($this->cp[$i1] >= 0) { $this->s[$i1][$k] = 1.0; $this->row[$i1] = $k - 1; $this->a[$i1] = 1; $k++; } } // 目的関数 if ($this->m_a == 0) { for ($i1 = 0; $i1 < $this->m; $i1++) $this->s[$this->n][$i1+1] = -$this->z[$i1]; } else { for ($i1 = 0; $i1 <= $this->m+$this->m_s; $i1++) { for ($i2 = 0; $i2 < $this->n; $i2++) { if ($this->a[$i2] > 0) $this->s[$this->n][$i1] -= $this->s[$i2][$i1]; } } } } /*******************************/ /* 最適化 */ /* sw : =0 : 中間結果無し */ /* =1 : 中間結果あり */ /* return : =0 : 正常終了 */ /* : =1 : 解無し */ /*******************************/ function optimize($sw) { // フェーズ1 if ($sw > 0) { if ($this->m_a > 0) printf("\nphase 1\n"); else printf("\nphase 2\n"); } $this->opt_run($sw); // フェーズ2 if ($this->err == 0 && $this->m_a > 0) { // 目的関数の変更 $this->mm -= $this->m_a; for ($i1 = 0; $i1 <= $this->mm; $i1++) $this->s[$this->n][$i1] = 0.0; for ($i1 = 0; $i1 < $this->n; $i1++) { $k = $this->row[$i1]; if ($k < $this->m) $this->s[$this->n][0] += $this->z[$k] * $this->s[$i1][0]; } for ($i1 = 0; $i1 < $this->mm; $i1++) { for ($i2 = 0; $i2 < $this->n; $i2++) { $k = $this->row[$i2]; if ($k < $this->m) $this->s[$this->n][$i1+1] += $this->z[$k] * $this->s[$i2][$i1+1]; } if ($i1 < $this->m) $this->s[$this->n][$i1+1] -= $this->z[$i1]; } // 最適化 if ($sw > 0) printf("\nphase 2\n"); $this->opt_run($sw); } return $this->err; } /*******************************/ /* 最適化(単体表の変形) */ /* sw : =0 : 中間結果無し */ /* =1 : 中間結果あり */ /*******************************/ function opt_run($sw) { $this->err = -1; while ($this->err < 0) { // 中間結果 if ($sw > 0) { printf("\n"); $this->output(); } // 列の選択(巡回を防ぐため必ずしも最小値を選択しない,Bland の規則) $q = -1; for ($i1 = 1; $i1 <= $this->mm && $q < 0; $i1++) { if ($this->s[$this->n][$i1] < -$this->eps) $q = $i1 - 1; } // 終了(最適解) if ($q < 0) $this->err = 0; // 行の選択( Bland の規則を採用) else { $p = -1; $k = -1; $min = 0.0; for ($i1 = 0; $i1 < $this->n; $i1++) { if ($this->s[$i1][$q+1] > $this->eps) { $x = $this->s[$i1][0] / $this->s[$i1][$q+1]; if ($p < 0 || $x < $min || $x == $min && $this->row[$i1] < $k) { $min = $x; $p = $i1; $k = $this->row[$i1]; } } } // 解無し if ($p < 0) $this->err = 1; // 変形 else { $x = $this->s[$p][$q+1]; $this->row[$p] = $q; for ($i1 = 0; $i1 <= $this->mm; $i1++) $this->s[$p][$i1] /= $x; for ($i1 = 0; $i1 <= $this->n; $i1++) { if ($i1 != $p) { $x = $this->s[$i1][$q+1]; for ($i2 = 0; $i2 <= $this->mm; $i2++) $this->s[$i1][$i2] -= $x * $this->s[$p][$i2]; } } } } } } /****************/ /* 単体表の出力 */ /****************/ function output() { for ($i1 = 0; $i1 <= $this->n; $i1++) { if ($i1 < $this->n) printf("x%d", $this->row[$i1]+1); else printf(" z"); for ($i2 = 0; $i2 <= $this->mm; $i2++) printf(" %f", $this->s[$i1][$i2]); printf("\n"); } } /**************/ /* 結果の出力 */ /**************/ function result() { if ($this->err > 0) printf("\n解が存在しません\n"); else { printf("\n("); for ($i1 = 0; $i1 < $this->m; $i1++) { $x = 0.0; for ($i2 = 0; $i2 < $this->n; $i2++) { if ($this->row[$i2] == $i1) { $x = $this->s[$i2][0]; break; } } if ($i1 == 0) printf("%f", $x); else printf(", %f", $x); } printf(") のとき,最大値 %f\n", $this->s[$this->n][0]); } } } // 入力 // 変数の数と式の数 fscanf(STDIN, "%d %d", $m, $n); $cp = array($n); $z = array($m); $eq_l = array($n); $eq_r = array($n); for ($i1 = 0; $i1 < $n; $i1++) $eq_l[$i1] = array($m); // 目的関数の係数 $str = trim(fgets(STDIN)); $z[0] = floatval(strtok($str, " ")); for ($i1 = 1; $i1 < $m; $i1++) $z[$i1] = floatval(strtok(" ")); // 制約条件 for ($i1 = 0; $i1 < $n; $i1++) { $str = trim(fgets(STDIN)); $eq_l[$i1][0] = floatval(strtok($str, " ")); for ($i2 = 1; $i2 < $m; $i2++) $eq_l[$i1][$i2] = floatval(strtok(" ")); $c = strtok(" "); if ($c == '<') $cp[$i1] = -1; else if ($c == '>') $cp[$i1] = 1; else $cp[$i1] = 0; $eq_r[$i1] = floatval(strtok(" ")); } // 実行 $lp = new LP($n, $m, $z, $eq_l, $eq_r, $cp); $sw = $lp->optimize(1); // 結果の出力 $lp->result(); ?>