1.プログラムの概要
このプログラムは、線形計画法における、最大化関数の最適解を求めるプログラムである。線形計画法(最大化の場合)では、最大化関数と制約条件がある。最大化関数は、1次関数であり、最大化を目的とする関数である。制約条件は、1次不等式や1次等式であり、最大化関数の解の範囲を定める式である。ただし、このプログラムでは、最大化関数も制約条件もそれぞれ2変数(X,Y)とする(このプログラムにおける、最大化関数と制約条件の詳細については、3章の操作方法を参照)。最適解を求めるアルゴリズムとして、シンプレックス法がある。シンプレックス法は、実行可能領域のある点をスタート地点として、その点を最大化関数が大きくなる方に移動させることを繰り返す。点の移動が止まったときに、最大化関数が最適解となる。このプログラムでは、ユーザが最大化関数と制約条件を入力して定める。そして、入力された最大化関数と制約条件をもとに、シンプレックス法による点の移動をシミュレーションし、最適解を求め表示させる。ただし、このプログラムは、あくまでGUIでシンプレックス法の点の移動をシミュレーションしているだけであり、実際にシンプレックス法を使って最適解を求めている訳ではない。最適解については、実行可能領域の各頂点の座標を最大化関数に代入し、最も大きい値となった解を最適解として表示させている。
2.プログラムの構造
プログラムの構造は以下の通りである。
1.Simplex_simuクラス
1.mainメソッド
2.MyPanel7クラス
1.コンストラクタ
2.paintComponentメソッド
3.runメソッド
4.actionPerformedメソッド
3.クラスの処理内容
それぞれの処理内容について説明していく。コード(5章)と見比べながら、読んで頂きたい。
3.1.Simplex_simuクラス
このクラスのmainメソッドでは、MyPanel7クラスの機能によって表示されたパネルをウィンドウに貼り付けている。また、ユーザが操作できるように、ウィンドウの大きさや閉じるボタンが押された時の処理の設定、ウィンドウを可視状態にするなど、ウィンドウの基本的な処理を記述している。
3.2.MyPanel7クラス
コンストラクタでは、最大化関数と制約条件を入力するためのテキストフィールドを宣言・配置している。また、ユーザが、どこに何を入力すれば良いかを、わかりやすくするために、入力を促すためのラベルも宣言・配置している。そして、入力した値をプログラムに反映させ、グラフに直線を書き込むためのボタン(button1)、シミュレーションを開始させるためのボタン(button2)の、2つのボタンも宣言・配置している。このように、コンストラクタでは、主に画面内の入力部分について記述している。
actionPerformedメソッドでは、ボタンがクリックされた時の処理を記述している。button1がクリックされた場合、テキストフィールドから値を取り出し、配列infoに格納させる。button2がクリックされた場合、シンプレックス法のシミュレーションを開始する。
runメソッドでは、スレッドを100ms停止させている。100ms停止させることで、シミュレーションの際に、ユーザが点の動きを確認できるようにしている。
そして、paintComponentメソッドでは、シミュレーション(点の移動)と最適解の計算の処理を行っている。具体的に、どのような記述をして処理を行っているかは、ソースコードのコメント文を読んで頂きたい。ここでは、大まかな処理について説明する。まず、入力された3つの制約条件から、グラフ上に3つの直線を引く。そして、3つの直線から、x切片、y切片、交点をそれぞれ求める。それらの点から、実行可能領域に含まれない点を座標(-1,-1)に設定して、除外する。ここで除外されなかった点は、実行可能領域の頂点になり、最適解の候補である。このようにして、求まった最適解の候補から、最大の解を与えるものを探索する。最大の解を与えるものが最適解となる。最適解が求まったので、後はシミュレーションを行う。まず、最大化関数の係数に着目し、係数が大きい方の変数を増やしていく。つまり、原点をスタート地点として、係数が大きい方の変数の切片まで点を移動させる。この際、その切片が最適解を与えるなら、そこで止まるようにする。そして、切片まで移動させたら、その切片から近い、2直線の交点まで移動させる。この交点が最適解を与えるなら、そこで止まるようにする。そして、さらに次の交点まで移動させる。この交点が最適解を与えるなら、そこで止まるようにする。このように、最適解となる点が実行可能領域内のどの点に位置するかによって、条件分岐し、点の動きが決まる。そして、最適解となる点で止まったら、グラフの右上に、最適解を表示させて、処理は終了である。
4.操作方法
目的関数を、S₁X+S₂Yとした場合に、X,Yの係数となるS₁とS₂を、ユーザが入力フィールドから、入力して決めることができる。ただし、S₁とS₂に入力する値は、正の整数に限る。また、実行可能領域を定めるための制約条件は、都合上、3式に限定した。3式をαiX+βiY ≦γiとした(1≦i≦3)場合に、それぞれαi、βi、γiを、ユーザが入力フィールドから、入力して決めることができる(都合上、不等号は3式とも≦に定めた)。ただし、これらの値も、正の整数に限る。最大化関数と制約条件の入力が済んだら、ボタン「①入力値を設定し、グラフに出力」をクリックする。このボタンをクリックすることで、入力した値がプログラムの各変数に代入される。そして、制約条件が、グラフに書き出される。次に、ボタン「②シミュレーションスタート」をクリックする。このボタンをクリックすることで、原点をスタート地点として、シンプレックス法による点の移動が始まる。そして、点が止まったところの(X,Y)が、最大化関数の最適解を与える。このときの最適解がグラフの右上に表示されて、プログラムは終了する。これが、このプログラムの操作方法である。再び、数字を入力し、2つのボタンを順にクリックすることで、何度でも実行可能である。
5.コード
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.Font;
public class Simplex_simu {
public static void main(String[] args) {
JFrame frame = new JFrame(); //フレーム生成
frame.setTitle("シンプレックス法 シミュレーション"); //ウィンドウタイトル
frame.setSize(675, 680); //ウィンドウサイズ
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //×ボタンが押された時の処理
MyPanel panel = new MyPanel(); // MyPanelを生成
frame.getContentPane().add(panel); // フレームにMyPanelを貼り付ける
frame.setVisible(true); //ウィンドウを可視状態にする
}
}
class MyPanel extends JPanel implements Runnable,ActionListener {
int count = 0;
int info[] = new int[11];
int simu_judge = 0; //シミュレーションを開始するかどうかを判定するための変数
//simu_judge=1で、シミュレーションを開始する
int scale_judge = 0; //目盛りをグラフの軸に書き込むかどうかを判定するための変数
//scale_judge=1で、シミュレーションを開始する
//円(そのときの解を示す)の中心座標
int x = 100;
int y = 600;
//最適解を求めるステップ数を格納する(step=4で、最適解が求まり終了する)
int step = 0;
//3つの直線の交点のx,y座標を格納する変数
int x_intersection12 = -1; //1番目に入力した直線と2番目に入力した直線の交点のx座標
int x_intersection13 = -1; //1番目に入力した直線と3番目に入力した直線の交点のx座標
int x_intersection23 = -1; //2番目に入力した直線と3番目に入力した直線の交点のx座標
int y_intersection12 = -1; //1番目に入力した直線と2番目に入力した直線の交点のy座標
int y_intersection13 = -1; //1番目に入力した直線と3番目に入力した直線の交点のy座標
int y_intersection23 = -1; //2番目に入力した直線と3番目に入力した直線の交点のy座標
//テキストフィールド(最大化関数)
JTextField o_x = new JTextField(10);
JTextField o_y = new JTextField(10);
JTextField o_n = new JTextField(10);
//テキストフィールド(制約条件1、1番目に入力する直線)
JTextField s_x1 = new JTextField(10);
JTextField s_y1 = new JTextField(10);
JTextField s_n1= new JTextField(10);
//テキストフィールド(制約条件2、2番目に入力する直線)
JTextField s_x2 = new JTextField(10);
JTextField s_y2 = new JTextField(10);
JTextField s_n2 = new JTextField(10);
//テキストフィールド(制約条件3、3番目に入力する直線)
JTextField s_x3 = new JTextField(10);
JTextField s_y3 = new JTextField(10);
JTextField s_n3 = new JTextField(10);
//ラベル(画面内で入力を促すために、案内するもの)
JLabel label1 = new JLabel("x +");
JLabel label2 = new JLabel("y");
JLabel label3 = new JLabel("");
JLabel label4 = new JLabel("x +");
JLabel label5 = new JLabel("y ≦");
JLabel label6 = new JLabel("x +");
JLabel label7 = new JLabel("y ≦");
JLabel label8 = new JLabel("x +");
JLabel label9 = new JLabel("y ≦");
JLabel label10 = new JLabel("x≧0、y≧0");
JLabel label11 = new JLabel("目的関数(最大化関数)");
JLabel label12 = new JLabel("制約条件");
//ボタン1(入力した値をプログラムに反映させ、グラフに直線を書き込む)
JButton button1 = new JButton("①入力値を設定し、グラフに出力");
//ボタン2(シンプレックス法のシミュレーションを開始する)
JButton button2 = new JButton("②シミュレーションスタート ");
public MyPanel() {
//イベントリスナを登録
button1.addActionListener(this);
button2.addActionListener(this);
//パネルのレイアウト設定
JPanel panel1 = new JPanel();
panel1.setLayout(new GridLayout(1,1));
JPanel panel2 = new JPanel();
panel2.setLayout(new GridLayout(1,5));
JPanel panel3 = new JPanel();
panel3.setLayout(new GridLayout(1,1));
JPanel panel4 = new JPanel();
panel4.setLayout(new GridLayout(1,5));
JPanel panel5 = new JPanel();
panel5.setLayout(new GridLayout(1,5));
JPanel panel6 = new JPanel();
panel6.setLayout(new GridLayout(1,5));
JPanel panel7 = new JPanel();
panel7.setLayout(new GridLayout(1,1));
//各パネルの色を白色に統一
panel1.setBackground(Color.WHITE);
panel2.setBackground(Color.WHITE);
panel3.setBackground(Color.WHITE);
panel4.setBackground(Color.WHITE);
panel5.setBackground(Color.WHITE);
panel6.setBackground(Color.WHITE);
panel7.setBackground(Color.WHITE);
//各コンポーネントを、それぞれのパネルに加える
panel1.add(label11);
panel2.add(o_x);
panel2.add(label1);
panel2.add(o_y);
panel2.add(label2);
panel2.add(label3);
panel3.add(label12);
panel4.add(s_x1);
panel4.add(label4);
panel4.add(s_y1);
panel4.add(label5);
panel4.add(s_n1);
panel5.add(s_x2);
panel5.add(label6);
panel5.add(s_y2);
panel5.add(label7);
panel5.add(s_n2);
panel6.add(s_x3);
panel6.add(label8);
panel6.add(s_y3);
panel6.add(label9);
panel6.add(s_n3);
panel7.add(label10);
//それぞれのパネルを、1つのパネルf_panel1に貼り付ける
JPanel f_panel1 = new JPanel();
f_panel1.setLayout(new GridLayout(9,1));
f_panel1.add(panel1);
f_panel1.add(panel2);
f_panel1.add(panel3);
f_panel1.add(panel4);
f_panel1.add(panel5);
f_panel1.add(panel6);
f_panel1.add(panel7);
f_panel1.add(button1);
f_panel1.add(button2);
f_panel1.setBackground(Color.WHITE); //f_panel1を白色に設置する
add(f_panel1); //f_panel1をMyPanelに加える
setBackground(Color.WHITE);
Thread refresh = new Thread(this);
refresh.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // コンポーネントを背景色で塗りつぶす
int seppen[] = new int[6];
int seppen_max = 0; //切片(x,y両方)の最大値
int xy_max = 0; //グラフにおけるx,yの最大値
int x_min = 0; //x切片の最小値
int y_min = 0; //y切片の最小値
int n_x_min = 0; //x切片の最小値を持つ式の番号
int n_y_min = 0; //y切片の最小値を持つ式の番号
double d_masu = 0; //1画素あたりのグラフ単位
int select3 = 0; //ステップ3において、まだ移動していない残りの交点に関与する直線の番号を格納する
//一時的なx切片、y切片の値を求め、各変数に代入する(10倍かけてint型にキャストすることで、小数点1桁までを変数に代入する)
int temporary_x1 = (int)(((double)info[4]/(double)info[2])*10); //1番目に入力された直線のx切片
int temporary_y1 = (int)(((double)info[4]/(double)info[3])*10); //1番目に入力された直線のy切片
int temporary_x2 = (int)(((double)info[7]/(double)info[5])*10); //2番目に入力された直線のx切片
int temporary_y2 = (int)(((double)info[7]/(double)info[6])*10); //2番目に入力された直線のy切片
int temporary_x3 = (int)(((double)info[10]/(double)info[8])*10); //3番目に入力された直線のx切片
int temporary_y3 = (int)(((double)info[10]/(double)info[9])*10); //3番目に入力された直線のy切片
//上で求めた切片を、配列seppen[]に格納する
seppen[0] = temporary_x1;
seppen[1] = temporary_y1;
seppen[2] = temporary_x2;
seppen[3] = temporary_y2;
seppen[4] = temporary_x3;
seppen[5] = temporary_y3;
//切片の最大値を求める。
seppen_max = seppen[0];
for(int i=1; i<6; i++){
if(seppen[i] > seppen_max){
seppen_max = seppen[i];
}
}
//切片の最大値から、グラフの目盛りの最大値を求める。
while(true){
//グラフの目盛りの最大値が、切片の最大値を超えた場合
if(xy_max >= seppen_max){
break; //ループを抜け出し、その時点でのxy_maxを目盛りの最大値とする
}
xy_max = xy_max + 150; //xy_maxを150ずつ増やす
//(切片は上部で10倍されているので、実際は目盛りの最大値は15ずつ増える)
}
//1画素あたりのグラフ単位を求める
d_masu = ((double)300)/((double)xy_max)*10; //300はグラフの縦方向の画素数
//300を目盛りの最大値で割り、10倍かけることで、グラフ単位の値が求まる
//各切片のグラフでの座標を求める
int decisive_x1 = (int)(((double)info[4]/(double)info[2])*d_masu); //1番目に入力された直線のx切片
int decisive_y1 = (int)(((double)info[4]/(double)info[3])*d_masu); //1番目に入力された直線のy切片
int decisive_x2 = (int)(((double)info[7]/(double)info[5])*d_masu); //2番目に入力された直線のx切片
int decisive_y2 = (int)(((double)info[7]/(double)info[6])*d_masu); //2番目に入力された直線のy切片
int decisive_x3 = (int)(((double)info[10]/(double)info[8])*d_masu); //3番目に入力された直線のx切片
int decisive_y3 = (int)(((double)info[10]/(double)info[9])*d_masu); //3番目に入力された直線のy切片
//入力する際の注意事項
g.drawString("※全ての入力フィールドには、必ず正の整数を入力すること",120,640);
g.drawLine(100,275,100,600); //y軸
g.drawLine(100,600,425,600); //x軸
g.drawLine(95,300,105,300); //y軸の目盛り(上から1番目)
g.drawLine(95,400,105,400); //y軸の目盛り(上から2番目)
g.drawLine(95,500,105,500); //y軸の目盛り(上から3番目)
g.drawLine(200,595,200,605); //x軸の目盛り(左から1番目)
g.drawLine(300,595,300,605); //x軸の目盛り(左から2番目)
g.drawLine(400,595,400,605); //x軸の目盛り(左から3番目)
//y軸矢印
g.drawLine(100,275,95,280);
g.drawLine(100,275,105,280);
//x軸矢印
g.drawLine(425,600,420,595);
g.drawLine(425,600,420,605);
//入力された3本の直線をグラフの描画
g.drawLine(decisive_x1+100,600,100,600-decisive_y1);
g.drawLine(decisive_x2+100,600,100,600-decisive_y2);
g.drawLine(decisive_x3+100,600,100,600-decisive_y3);
//各目盛りの値を計算する
int n_xy1 = xy_max/30;
int n_xy2 = (xy_max/30)*2;
int n_xy3 = xy_max/10;
//各目盛りの数値を、文字列に変換する(各目盛りの数値の桁数を数えるため)
String s_xy1 = String.valueOf(n_xy1);
String s_xy2 = String.valueOf(n_xy2);
String s_xy3 = String.valueOf(n_xy3);
//各目盛りの数値の桁数を取得し、代入する(桁数によって、目盛りの数値の位置が変わらないようにするため)
int xy1_length = String.valueOf(n_xy1).length();
int xy2_length = String.valueOf(n_xy2).length();
int xy3_length = String.valueOf(n_xy3).length();
//bottan1が入力された場合(各目盛りの値をグラフに書き込む)
if(scale_judge == 1){
g.drawString(s_xy1, 200-(xy1_length*3), 620);
g.drawString(s_xy2, 300-(xy2_length*3), 620);
g.drawString(s_xy3, 400-(xy3_length*3), 620);
g.drawString(s_xy1, 90-(xy1_length*6), 505);
g.drawString(s_xy2, 90-(xy2_length*6), 405);
g.drawString(s_xy3, 90-(xy3_length*6), 305);
}
//x切片とy切片の最小値を求める。また、その最小値を持つ直線の番号(何番目に入力された)を求める
x_min = decisive_x1;
n_x_min = 1;
y_min = decisive_y1;
n_y_min = 1;
if(x_min > decisive_x2){
x_min = decisive_x2;
n_x_min = 2;
}
if(x_min > decisive_x3){
x_min = decisive_x3;
n_x_min = 3;
}
if(y_min > decisive_y2){
y_min = decisive_y2;
n_y_min = 2;
}
if(y_min > decisive_y3){
y_min = decisive_y3;
n_y_min = 3;
}
//各直線(制約条件による直線)の上限の値(定数項)を、グラフ単位の値に変換し、代入する
int upper1 = (int)(((double)info[4])*d_masu);
int upper2 = (int)(((double)info[7])*d_masu);
int upper3 = (int)(((double)info[10])*d_masu);
//各直線の交点のx座標を求める
x_intersection12 = (int)((((double)info[7]/(double)info[6])-((double)info[4]/(double)info[3]))/(((double)info[5]/(double)info[6])-((double)info[2]/(double)info[3]))*d_masu);
x_intersection13 = (int)((((double)info[10]/(double)info[9])-((double)info[4]/(double)info[3]))/(((double)info[8]/(double)info[9])-((double)info[2]/(double)info[3]))*d_masu);
x_intersection23 = (int)((((double)info[10]/(double)info[9])-((double)info[7]/(double)info[6]))/(((double)info[8]/(double)info[9])-((double)info[5]/(double)info[6]))*d_masu);
//各直線の交点のy座標を求める
y_intersection12 = (int)((((double)info[7]/(double)info[5])-((double)info[4]/(double)info[2]))/(((double)info[6]/(double)info[5])-((double)info[3]/(double)info[2]))*d_masu);
y_intersection13 = (int)((((double)info[10]/(double)info[8])-((double)info[4]/(double)info[2]))/(((double)info[9]/(double)info[8])-((double)info[3]/(double)info[2]))*d_masu);
y_intersection23 = (int)((((double)info[10]/(double)info[8])-((double)info[7]/(double)info[5]))/(((double)info[9]/(double)info[8])-((double)info[6]/(double)info[5]))*d_masu);
//交点がない場合、その座標を(-1,-1)に設定する。(-1,-1)は実行可能領域の範囲外
//1と2の直線が平行な場合
if(x_intersection12>2000000000 || y_intersection12>2000000000){
x_intersection12 = -1;
y_intersection12 = -1;
}
//1と3の直線が平行な場合
if(x_intersection13>2000000000 || y_intersection13>2000000000){
x_intersection13 = -1;
y_intersection13 = -1;
}
//2と3の直線が平行な場合
if(x_intersection23>2000000000 || y_intersection23>2000000000){
x_intersection23 = -1;
y_intersection23 = -1;
}
//1と2の交点が実行可能領域の範囲外にある場合
if(info[8]*x_intersection12+info[9]*y_intersection12 > upper3){
x_intersection12 = -1;
y_intersection12 = -1;
}
//1と3の交点が実行可能領域の範囲外にある場合
if(info[5]*x_intersection13+info[6]*y_intersection13 > upper2){
x_intersection13 = -1;
y_intersection13 = -1;
}
//2と3の交点が実行可能領域の範囲外にある場合
if(info[2]*x_intersection23+info[3]*y_intersection23 > upper1){
x_intersection23 = -1;
y_intersection23 = -1;
}
int answer_x = info[0]*x_min; //x切片(最小値)での解
int answer_y = info[1]*y_min; //x切片(最小値)での解
//各直線の交点での解(初期値は-1)
int answer12 = -1;
int answer13 = -1;
int answer23 = -1;
//各直線の交点での解が、実行可能領域内にある場合に、その解を各変数に代入する
if(x_intersection12>0 && y_intersection12>0){
answer12 = info[0]*x_intersection12+info[1]*y_intersection12;
}
if(x_intersection13>0 && y_intersection13>0){
answer13 = info[0]*x_intersection13+info[1]*y_intersection13;
}
if(x_intersection23>0 && y_intersection23>0){
answer23 = info[0]*x_intersection23+info[1]*y_intersection23;
}
//上で求めた解の中から、最大値の解(最適解)を探し、answer_maxに代入する
int answer_max = answer_x;
if(answer_max < answer_y){
answer_max = answer_y;
}
if(answer_max < answer12){
answer_max = answer12;
}
if(answer_max < answer13){
answer_max = answer13;
}
if(answer_max < answer23){
answer_max = answer23;
}
//シミュレーションを開始するボタンがクリックされた場合
if(simu_judge == 1){
g.setColor(Color.red); //描画する円の色を赤色に設定する
g.fillOval(x-4, y-4, 8, 8); //円を描画する((x-4,y-4)が円の中心)
//ステップ1(原点から切片の移動)
if(step == 1){
//目的関数のy切片の係数の方が大きい場合
if(info[0] <= info[1]){
y = y-1; //原点からy軸に沿って移動する
//点がy切片の最小値に到達した場合
if(y == 600-y_min){
//その点が最適解を与える場合
if(answer_max == answer_y){
step = 4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step = 2; //ステップ2に移る
}
}
}
//目的関数のy切片の係数の方が小さい場合
else if(info[0] > info[1]){
x = x+1; //原点からx軸に沿って移動する
//点がx切片の最小値に到達した場合
if(x == x_min+100){
//その点が最適解を与える場合
if(answer_max == answer_x){
step = 4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step = 2; //ステップ2に移る
}
}
}
}
//ステップ2(切片から交点への移動)
else if(step == 2){
//目的関数のy切片の係数の方が大きい場合
if(info[0] <= info[1]){
//直線1,2の交点に到達した場合(実行可能領域の範囲内に限る)
if(x_intersection12>0 && x>x_intersection12+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection12+100));
y = y-(y - (600-y_intersection12));
//その点が最適解を与える場合
if(answer12 > answer13 && answer12 > answer23){
step = 4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step = 3; //ステップ3に移る
select3 = 1; //残りの交点を探す手がかりとして、直線1を記憶する
}
}
//直線1,3の交点に到達した場合(実行可能領域の範囲内に限る)
else if(x_intersection13>0 && x>x_intersection13+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection13+100));
y = y-(y - (600-y_intersection13));
//その点が最適解を与える場合
if(answer13 > answer12 && answer13 > answer23){
step=4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step = 3; //ステップ3に移る
select3 = 2; //残りの交点を探す手がかりとして、直線2を記憶する
}
}
//直線2,3の交点に到達した場合(実行可能領域の範囲内に限る)
else if(x_intersection23>0 && x>x_intersection23+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection23+100));
y = y-(y - (600-y_intersection23));
//その点が最適解を与える場合
if(answer23 > answer12 && answer23 > answer13){
step=4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step = 3; //ステップ3に移る
select3 = 3; //残りの交点を探す手がかりとして、直線3を記憶する
}
}
//最適解がx切片で、その点に到達した場合
else if(answer_max == answer_x && y>600){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_min+100));
y = y-(y - 600);
step = 4; //ステップ4に移る
}
//この3つの分岐は、各直線に対応した座標の変位を与える(その直線上を移動するように)
else if(n_y_min == 1){
x = x+info[3];
y = y+info[2];
}
else if(n_y_min == 2){
x = x+info[6];
y = y+info[5];
}
else if(n_y_min == 3){
x = x+info[9];
y = y+info[8];
}
}
//目的関数のy切片の係数の方が小さい場合
else if(info[0] > info[1]){
//直線1,2の交点に到達した場合(実行可能領域の範囲内に限る)
if(y_intersection12>0 && y<600-y_intersection12){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection12+100)-x);
y = y + ((600-y_intersection12)-y);
//その点が最適解を与える場合
if(answer12 > answer13 && answer12 > answer23){
step=4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step=3; //ステップ3に移る
select3 = 1; //残りの交点を探す手がかりとして、直線1を記憶する
}
}
//直線1,3の交点に到達した場合(実行可能領域の範囲内に限る)
else if(y_intersection13>0 && y<600-y_intersection13){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection13+100)-x);
y = y + ((600-y_intersection13)-y);
//その点が最適解を与える場合
if(answer13 > answer12 && answer13 > answer23){
step=4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step=3; //ステップ3に移る
select3 = 2; //残りの交点を探す手がかりとして、直線2を記憶する
}
}
//直線2,3の交点に到達した場合(実行可能領域の範囲内に限る)
else if(y_intersection23>0 && y<600-y_intersection23){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection23+100)-x);
y = y + ((600-y_intersection23)-y);
//その点が最適解を与える場合
if(answer23 > answer12 && answer23 > answer13){
step=4; //ステップ4に移る
}
//その点が最適解を与えない場合
else{
step=3; //ステップ3に移る
select3 = 3; //残りの交点を探す手がかりとして、直線3を記憶する
}
}
//最適解がy切片で、その点に到達した場合
else if(answer_max == answer_y && x<100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + (100-x);
y = y + ((600-y_min)-y);
step = 4; //ステップ4に移る
}
//この3つの分岐は、各直線に対応した座標の変位を与える(その直線上を移動するように)
else if(n_x_min == 1){
x = x-info[3];
y = y-info[2];
}
else if(n_x_min == 2){
x = x-info[6];
y = y-info[5];
}
else if(n_x_min == 3){
x = x-info[9];
y = y-info[8];
}
}
}
//ステップ3(交点から交点への移動)
else if(step == 3){
//目的関数のy切片の係数の方が大きい場合
if(info[0] <= info[1]){
//残りの交点である、直線1,3の交点に到達し、その点が実行解である場合
if(select3 == 1 && x_intersection13>0 && x>x_intersection13+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection13+100));
y = y-(y - (600-y_intersection13));
step=4; //ステップ4に移る
}
//残りの交点である、直線2,3の交点に到達し、その点が実行解である場合
else if(select3 == 1 && x_intersection23>0 && x>x_intersection23+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection23+100));
y = y-(y - (600-y_intersection23));
step=4; //ステップ4に移る
}
//残りの交点である、直線1,2の交点に到達し、その点が実行解である場合
else if(select3 == 2 && x_intersection12>0 && x>x_intersection12+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection12+100));
y = y-(y - (600-y_intersection12));
step=4; //ステップ4に移る
}
//残りの交点である、直線2,3の交点に到達し、その点が実行解である場合
else if(select3 == 2 && x_intersection23>0 && x>x_intersection23+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection23+100));
y = y-(y - (600-y_intersection23));
step=4; //ステップ4に移る
}
//残りの交点である、直線1,2の交点に到達し、その点が実行解である場合
else if(select3 == 3 && x_intersection12>0 && x>x_intersection12+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection12+100));
y = y-(y - (600-y_intersection12));
step=4; //ステップ4に移る
}
//残りの交点である、直線1,3の交点に到達し、その点が実行解である場合
else if(select3 == 3 && x_intersection13>0 && x>x_intersection13+100){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x-(x - (x_intersection13+100));
y = y-(y - (600-y_intersection13));
step=4; //ステップ4に移る
}
//この3つの分岐は、各直線に対応した座標の変位を与える(その直線上を移動するように)
else if(n_y_min == 1){
if(n_x_min == 2){
x = x+info[9];
y = y+info[8];
}
else if(n_x_min == 3){
x = x+info[6];
y = y+info[5];
}
}
else if(n_y_min == 2){
if(n_x_min == 1){
x = x+info[9];
y = y+info[8];
}
else if(n_x_min == 3){
x = x+info[3];
y = y+info[2];
}
}
else if(n_y_min == 3){
if(n_x_min == 1){
x = x+info[6];
y = y+info[5];
}
else if(n_x_min == 2){
x = x+info[3];
y = y+info[2];
}
}
}
//目的関数のy切片の係数の方が小さい場合
else if(info[0] > info[1]){
if(select3 == 1 && y_intersection13>0 && y<600-y_intersection13){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection13+100)-x);
y = y + ((600-y_intersection13)-y);
step=4; //ステップ4に移る
}
else if(select3 == 1 && y_intersection23>0 && y<600-y_intersection23){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection23+100)-x);
y = y + ((600-y_intersection23)-y);
step=4; //ステップ4に移る
}
else if(select3 == 2 && y_intersection12>0 && y<600-y_intersection12){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection12+100)-x);
y = y + ((600-y_intersection12)-y);
step=4; //ステップ4に移る
}
else if(select3 == 2 && y_intersection23>0 && y<600-y_intersection23){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection23+100)-x);
y = y + ((600-y_intersection23)-y);
step=4; //ステップ4に移る
}
else if(select3 == 3 && y_intersection12>0 && y<600-y_intersection12){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection12+100)-x);
y = y + ((600-y_intersection12)-y);
step=4; //ステップ4に移る
}
else if(select3 == 3 && y_intersection13>0 && y<600-y_intersection13){
//到達した際に、丁度その点が円の中心座標になるように、補正する
x = x + ((x_intersection13+100)-x);
y = y + ((600-y_intersection13)-y);
step=4; //ステップ4に移る
}
//この3つの分岐は、各直線に対応した座標の変位を与える(その直線上を移動するように)
else if(n_x_min == 1){
if(n_y_min == 2){
x = x-info[9];
y = y-info[8];
}
else if(n_y_min == 3){
x = x-info[6];
y = y-info[5];
}
}
else if(n_x_min == 2){
if(n_y_min == 1){
x = x-info[9];
y = y-info[8];
}
else if(n_y_min == 3){
x = x-info[3];
y = y-info[2];
}
}
else if(n_x_min == 3){
if(n_x_min == 1){
x = x-info[6];
y = y-info[5];
}
else if(n_x_min == 2){
x = x-info[3];
y = y-info[2];
}
}
}
}
//ステップ4(点(円)の移動が止まり、最適解が求まる。最適解を表示する)
else if(step == 4){
//表示する最適解の値の色を、黒色に設定する
g.setColor(Color.black);
//フォントサイズを設定し、見えやすいようにする
Font fo = new Font("Serif",Font.PLAIN,20);
g.setFont(fo);
//グラフ単位の最適解を、問題の単位に直す
double best_answer = ((double)answer_max/d_masu);
//最適解の値を文字列に変換する
String ba = String.valueOf(best_answer);
//最適解を表示する
g.drawString("最適解はおおよそ",450,325);
g.drawString(String.format("%.2f", best_answer), 475, 350);
}
}
}
//円を動かすために、スレッドの実行間隔を制御する
public void run() {
while(true) {
repaint();
try {
Thread.sleep(300); //スレッドを100ms停止する
}catch(Exception e) {
System.out.println(e);
}
}
}
public void actionPerformed(ActionEvent ae){
//button1がクリックされた場合
if(ae.getSource() == button1){
//テキストフィールドから入力された値を、配列infoに格納する
info[0] = Integer.parseInt(o_x.getText());
info[1] = Integer.parseInt(o_y.getText());
info[2] = Integer.parseInt(s_x1.getText());
info[3] = Integer.parseInt(s_y1.getText());
info[4] = Integer.parseInt(s_n1.getText());
info[5] = Integer.parseInt(s_x2.getText());
info[6] = Integer.parseInt(s_y2.getText());
info[7] = Integer.parseInt(s_n2.getText());
info[8] = Integer.parseInt(s_x3.getText());
info[9] = Integer.parseInt(s_y3.getText());
info[10] = Integer.parseInt(s_n3.getText());
scale_judge = 1; //scale_judge=1に設定し、グラフに制約条件の3つの直線を書き込む
}
//button2がクリックされた場合
else if(ae.getSource() == button2){
//円の座標を、グラフの原点に設定する
x = 100;
y = 600;
step = 1; //シミュレーションのステップを1に設定する
simu_judge = 1; //simu_judgeを1に設定し、シミュレーションを開始する
}
}
}