6.2 Java

Javaの良いところは、GUIのプログラムがほどほどの手間で書けて、 それはシミュレーションの便利さに通じる (例えばパラメーターを調節するとか) ことでしょうか。

(2021/4/13追記) 以下のサンプル・プログラムは、Java アプレットであるが、 最近の Java ではアプレットは廃止されてしまった。 こういうのが WWW で試せるのは面白いと思ったのだけれど… (直す気はなくなった。)


/*
 * ConstLinear2D.java --- 2次元定数係数線型常微分方程式
 */

// <APPLET code="ConstLinear2D.class" width=500 height=500> </APPLET>

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

class GraphCanvas extends Canvas {

  static final boolean DEBUG = false; // true;
  private static final String message = "graph of a function with 1 variable";
  // 問題に取って基本的なパラメーター
  // 係数行列
  private double a, b, c, d;
  // 描画範囲
  private double x_max =   1.0, x_min = - 1.0, y_max = 1.0, y_min = - 1.0;
  private double x_margin = (x_max - x_min) / 10;
  private double y_margin = (y_max - y_min) / 10;

  // 座標系の変換のためのパラメーター
  private int CanvasX = 400, CanvasY = 400;
  private double ratiox, ratioy, X0, Y0;

  // コンストラクター
  public GraphCanvas() {
    super();
  }
  public GraphCanvas(int cx, int cy) {
    super();
    CanvasX = cx; CanvasY = cy;
  }

  public void compute(double A, double B, double C, double D) {
    a = A; b = B; c = C; d = D;
    repaint();
  }

  private boolean IsIn(double x, double y) {
    return (x_min <= x && x <= x_max && y_min <= y && y <= y_max);
  }
  // 座標変換の準備
  private void space(double x0, double y0, double x1, double y1) {
    X0 = x0; Y0 = y0;
    ratiox = CanvasX / (x1 - x0);
    ratioy = CanvasY / (y1 - y0);
  }
  // ユーザー座標 (ワールド座標系) をウィンドウ座標 (デバイス座標系)
  private int wx(double x) {
    return (int)(ratiox * (x - X0));
  }
  // ユーザー座標 (ワールド座標系) をウィンドウ座標 (デバイス座標系)
  private int wy(double y) {
    return CanvasY - (int)(ratioy * (y - Y0));
  }
  // 力学系の右辺 f=(fx, fy)
  private double fx(double x, double y) {
    return a * x + b * y;
  }
  private double fy(double x, double y) {
    return c * x + d * y;
  }
  // (x0,y0) を初期値とする解の軌道 (trajectory) を描く
  private void drawTrajectory(Graphics g, double x0, double y0, double T) {
    double x, y, new_x, new_y;
    double k1x, k1y, k2x, k2y, k3x, k3y, k4x, k4y;
    double h = 0.01 / Math.sqrt(a * a + b * b + c * c + d * d);
    if (T < 0.0)
      h = - h;
    int iter = (int)Math.rint(Math.abs(T / h));
    x = x0;
    y = y0;
    for (int i = 1; i <= iter; i++) {
      k1x = h * fx(x, y);
      k1y = h * fy(x, y);
      k2x = h * fx(x + k1x / 2, y + k1y / 2);
      k2y = h * fy(x + k1x / 2, y + k1y / 2);
      k3x = h * fx(x + k2x / 2, y + k2y / 2);
      k3y = h * fy(x + k2x / 2, y + k2y / 2);
      k4x = h * fx(x + k3x, y + k3y);
      k4y = h * fy(x + k3x, y + k3y);
      new_x = x + (k1x + 2 * k2x + 2 * k3x + k4x) / 6;
      new_y = y + (k1y + 2 * k2y + 2 * k3y + k4y) / 6;
      if (IsIn(x, y) && IsIn(new_x, new_y))
          g.drawLine(wx(x), wy(y), wx(new_x), wy(new_y));
      x = new_x; y = new_y;
    }
  }

  public void paint(Graphics g) {
    //
    space(x_min - x_margin, y_min - y_margin,
          x_max + x_margin, y_max + y_margin);
    //
    setBackground(Color.blue);
    //
    g.setColor(Color.black);
    g.drawLine(wx(x_min), wy(0.0), wx(x_max), wy(0.0));
    g.drawLine(wx(0.0), wy(y_min), wx(0.0), wy(y_max));
    //
    g.setColor(Color.yellow);
    int n = 36;
    double dt = 2 * Math.PI / n;
    double Time = 10.0 / Math.sqrt(a * a + b * b + c * c + d * d);
    for (int i = 0; i < n; i++) {
      double t = i * dt;
      drawTrajectory(g, Math.cos(t), Math.sin(t),   Time);
      drawTrajectory(g, Math.cos(t), Math.sin(t), - Time);
    }
  }
}

public class ConstLinear2D extends Applet implements ActionListener {

  private int N = 20;
  private double lambda = 0.5;
  private double Tmax = 0.5;
  // ユーザーとのインターフェイス (パラメーターの入力)
  private Label label_a, label_b, label_c, label_d;
  private TextField input_a, input_b, input_c, input_d;
  private double a = 1.0, b = 0.0, c = 0.0, d = 1.0;
  private Button startB;
  //
  private GraphCanvas gc;

  private void ReadFields() {
    a = Double.valueOf(input_a.getText()).doubleValue();
    b = Double.valueOf(input_b.getText()).doubleValue();
    c = Double.valueOf(input_c.getText()).doubleValue();
    d = Double.valueOf(input_d.getText()).doubleValue();
  }
  // 準備 (変数の用意と座標系の初期化など)
  public void init() {
    // ナル・レイアウト
    setLayout(null);
    // a, b, c, d を入力するためのテキスト・フィールド
    add(label_a = new Label("a=")); label_a.setBounds(100, 30, 40, 30);
    add(label_b = new Label("b=")); label_b.setBounds(250, 30, 40, 30);
    add(label_c = new Label("c=")); label_c.setBounds(100, 70, 40, 30);
    add(label_d = new Label("d=")); label_d.setBounds(250, 70, 40, 30);
    add(input_a = new TextField("" + a)); input_a.setBounds(150, 30, 100, 30);
    add(input_b = new TextField("" + b)); input_b.setBounds(300, 30, 100, 30);
    add(input_c = new TextField("" + c)); input_c.setBounds(150, 70, 100, 30);
    add(input_d = new TextField("" + d)); input_d.setBounds(300, 70, 100, 30);
    // 再計算ボタン
    startB = new Button("Restart");
    add(startB);
    startB.setBounds(420, 45, 50, 30);
    startB.addActionListener(this);

    // キャンバス
    gc = new GraphCanvas();
    add(gc);
    gc.setBounds(50, 100, 400, 400);
    ReadFields();
    gc.compute(a, b, c, d);
  }

  // ボタンを押されたら、テキスト・フィールドの内容を読み取って、再描画
  public void actionPerformed(ActionEvent e) {
    if (e.getSource() == startB) {
      ReadFields();
      gc.compute(a, b, c, d);
    }
  }
}

図 11: 定数係数線形常微分方程式を Java アプレットで解く
\includegraphics[scale=0.5]{ode_figure/ConstLinear2D.eps}

初期値の選択、時間の逆転、マウスで初期値、など拡張したい。



桂田 祐史