反復の回数が決まっている場合には,「FOR文」を用いることができます.
「FOR文」の基本的な構造は次の通りです.
for(初期化 ; 条件式 ; 再初期化){ 切り返す処理 }
関数の定積分の計算を考える.定積分は関数のグラフに対して,設定された範囲の面積を求めることに相当する.そこで,定積分の下限と上限2つの数値 \(A\),\(B\) を入力し,関数のグラフの面積を計算するプログラムを作ってみる.
\(x\)軸と\(ab\)間の函数\(f(x)\)のグラフに囲まれた面積は, \( x_i = a + i \Delta x \)(\(\Delta x\)は\(x\)の微少変化)として, \[ \lim_{\Delta x \rightarrow 0}\sum_{i=0}^{x_i = b}f(x_i)\Delta x \] のようにして計算できる.(これが関数の定積分の定義)
ここで,\(f(x_i)\Delta x\)は,\(x_i\) における横 \(\Delta x\),縦 \(f(x_i)\),の細長い長方形の面積(図の黄色い長方形)を表し,これらを\(a\)から\(b\)まで足し合わせて行けば図形の面積の近似値になり,この極限値が面積に一致する.
以上のことから,プログラムでは,\(ab\)間を\(n\)等分して,分割したそれぞれの位置での長方形の面積を求め,それらの和をとることで図形の面積の近似値を計算する.
\(i\) 個目の長方形の面積は, \[ \Delta S_i = f(x_i)\Delta x \] したがって,面積 \(S\) の近似値は,全ての長方形の面積を足し合わせて, \begin{align} S &= \Delta S_0 + \Delta S_1 + \Delta S_2 + \cdots + \Delta S_{n-1} = \sum_{i=0}^{n-1} \Delta S_i \\ &= \left( f(x_0) + f(x_1) + f(x_2) + \cdots + f(x_{n-1}) \right)\Delta x \end{align} となる.
上記の考え方を参考にして,具体例として円の面積を計算するプログラムを考えてみます.
半径 \(r\) の円の式は,以下の通りです. \[ x^2 + y^2 = r^2 \]
Javaで数学関数は,Mathパッケージに入っているので,Math.関数名(変数)のようにして使用できます.
/************************************************** 反復構造:for文 **************************************************/ import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; public class ThirdREP extends Application{ public static void main(String[] args) { launch(args); } TextField tf1 = new TextField(); TextField tf2 = new TextField(); Label label1 = new Label("半径:"); Label label2 = new Label("分割数:"); Label label3 = new Label(); @Override public void start(Stage stage) throws Exception { stage.setTitle("Application ThirdREP"); VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10,10,10,10)); root.setSpacing(10.0); HBox pane1 = new HBox(); pane1.setAlignment(Pos.CENTER); pane1.setPadding(new Insets(10,10,10,10)); pane1.setSpacing(5.0); tf1.setAlignment(Pos.CENTER_RIGHT); tf1.setMaxWidth(50); tf2.setAlignment(Pos.CENTER_RIGHT); tf2.setMaxWidth(80); pane1.getChildren().addAll(label1,tf1,label2,tf2); tf1.setOnAction(event -> calcArea()); tf2.setOnAction(event -> calcArea()); root.getChildren().addAll(pane1,label3); Scene scene = new Scene(root,400,200); stage.setScene(scene); stage.show(); } private void calcArea() { String s1 = tf1.getText(); double r = Double.parseDouble(s1); String s2 = tf2.getText(); int n = Integer.parseInt(s2); /******* for文のための設定 **************************************/ double a = 0.0; //定積分の下限の x 座標 double b = r; //定積分の上限の x 座標 double dx = (b - a)/n; // 分割した長方形の x 座標の変化量 double x1, y1; //分割した長方形の端点の座標を入れる変数 double s = 0.0; //それまでの長方形の面積の合計を入れておく変数 /********* for文 *******************************/ for(int i = 0 ; i < n ; i++){ x1 = a + i*dx; // i 番目の端点の x 座標 //数学関数の使用 y1 = Math.sqrt(r*r - x1*x1); // i 番目の端点の y 座標 // i 番目までの長方形の面積の和(前回までの合計に今回計算したものを加える) s = s + dx*y1; } /***************************************/ //上の計算で円の4分の1の面積が求められるので,4倍して表示する label3.setText("半径" + r + "の円の面積は" + 4*s + "です."); } }
例題と同様の考え方で,関数で表される曲線の長さを求めるプログラム考えることができる.楕円のx半径とy半径を表す2つの数値 \(A\),\(B\) と分割数 \(n\) を入力させて,楕円の周の長さを計算するプログラムを作りなさい.クラス名は,「Ex08_1」とする.
楕円を表す関数は次のように表される. \[ \frac{x^2}{A^2} + \frac{y^2}{B^2} = 1 \]
考え方は,曲線を細かく分割して,分割されたものの長さ全てを合計することによって求めます.どんな曲線も充分細かく分割すれば,一つ一つは線分(直線)と考えて良く,線分の端点の座標が分かっていれば,ピタゴラスの定理から長さが計算できるので,全ての線分の長さを求めて合計することによって曲線の長さを近似的に求めることが出来ます.
例えば,\(y=f(x)\) と表される曲線において,\(x = a\) から \(x = b\) までの部分の長さを \(n\) 等分して求めるとすると,分割した部分の \(x\) 方向の変化量は \(\Delta x = \dfrac{b-a}{n}\) となり,\(i\) 個目の部分の端点の \(x\) 座標は,それぞれ \(x_{i} = a + i \Delta x\) と \(x_{i+1} = a + (i+1)\Delta x\) .\(y\) 座標は,\(y_i = f(x_i)\) と \(y_{i+1} = f(x_{i+1})\) になるので.
\(i\) 個目の部分の線分の長さは, \[ \Delta s_i = \sqrt{(x_{i+1} - x_i)^2 + (y_{i+1} - y_i)^2} = \sqrt{(\Delta x)^2 + (f(x_{i+1}) - f(x_i))^2} \] したがって,曲線の長さ \(s\) の近似値は,全ての線分の長さを足し合わせて, \[ s = \Delta s_0 + \Delta s_1 + \Delta s_2 + \cdots + \Delta s_n = \sum_{i=0}^{n} \Delta s_i \] となる.
チャートとは日本語で言うところのグラフのことです.JavaFXには様々な種類のグラフを描くためのチャートのクラスが用意されています.
チャートクラスは,予め用意したデータやプログラム内で生成したデータを用いてグラフを作成することが出来ます.
種類 | 説明 |
---|---|
AreaChart | 面グラフ.塗りつぶした折れ線グラフ |
BarChart | 棒グラフ |
BubbleChart | 3次元のデータを表すバブルチャート |
LineChart | 折れ線グラフ |
PieChart | 円グラフ |
ScatterChart | 散布図 |
StackedAreaChart | 積み上げ面グラフ |
StackedBarChart | 積み上げ棒グラフ |
ここではLineChartを使用して,関数のグラフを描いて見ましょう.グラフを作るためのデータをfor文を使って用意します.
係数 \(a\),\(b\),\(c\) を入力して,関数: \( f(x) = a \cos{(bx + c)} \) のグラフを描きます.
/************************************************** 反復構造:for文 **************************************************/ import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; public class ChartEx extends Application{ public static void main(String[] args) { launch(args); } TextField tf1 = new TextField(); TextField tf2 = new TextField(); TextField tf3 = new TextField(); Label label1 = new Label("A = "); Label label2 = new Label("B = "); Label label3 = new Label("C = "); NumberAxis xaxis = new NumberAxis(); //横軸の作成 NumberAxis yaxis = new NumberAxis(); //縦軸の作成 //折れ線グラフの原型を作成 LineChart<Number,Number> linechart = new LineChart<>(xaxis,yaxis); //データ系列の原型を作成 XYChart.Series<Number, Number> xychartseries = new XYChart.Series<>(); @Override public void start(Stage stage) throws Exception { stage.setTitle("Application ChartEx"); VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10,10,10,10)); root.setSpacing(10.0); HBox pane1 = new HBox(); pane1.setAlignment(Pos.CENTER); pane1.setPadding(new Insets(10,10,10,10)); pane1.setSpacing(10.0); tf1.setAlignment(Pos.CENTER_RIGHT); tf1.setMaxWidth(50); tf2.setAlignment(Pos.CENTER_RIGHT); tf2.setMaxWidth(50); tf3.setAlignment(Pos.CENTER_RIGHT); tf3.setMaxWidth(50); pane1.getChildren().addAll(label1,tf1,label2,tf2,label3,tf3); tf1.setOnAction(event -> createData()); tf2.setOnAction(event -> createData()); tf3.setOnAction(event -> createData()); xaxis.setLabel("x"); //横軸のラベルを設定 yaxis.setLabel("y"); //縦軸のラベルを設定 linechart.setTitle("y = Acos(Bx+C)"); //グラフにタイトルを設定 linechart.setCreateSymbols(false); //データ記号を表示しない linechart.setLegendVisible(false); //凡例を表示しない linechart.setAnimated(false); //データ変更をアニメーション化しない linechart.getData().add(xychartseries); //作成したデータ系列を用いて折れ線グラフを作成 root.getChildren().addAll(pane1,linechart); Scene scene = new Scene(root,800,400); stage.setScene(scene); stage.show(); } private void createData() { String s1 = tf1.getText(); double a = Double.parseDouble(s1); String s2 = tf2.getText(); double b = Double.parseDouble(s2); String s3 = tf3.getText(); double c = Double.parseDouble(s3); /*********************************************/ double startx = -6.5; //最初の点の x 座標 double endx = 6.5; //最後の点の x 座標 int n = 300; //データ数 double dx = (endx - startx)/n; // x 座標の変化量 xychartseries.getData().clear(); //以前のデータ系列を消去 /********* for文 *******************************/ for(int i = 0 ; i <= n ; i++){ // i 毎にデータを作成し系列に加える xychartseries.getData().add(new XYChart.Data<>(startx + i*dx,a*Math.cos(b*(startx+i*dx)+c))); } } }
上のプログラムを参考にして,複数の関数を組み合わせた関数(足す,掛ける,合成するなど)のグラフを描くプログラムを作ってみましょう.クラス名は「Ex08_2」とせよ.
Javaに組み込まれている代表的な数学関数には次のようなものがあります.
メソッド | 説明 | メソッド | 説明 | |
---|---|---|---|---|
abs(x) | xの絶対値 | sqrt(x) | 平方根 | |
sin(x) | 三角関数:\(\sin{x}\) | exp(x) | 指数関数:\(e^x\) | |
cos(x) | 三角関数:\(\cos{x}\) | log(x) | 自然対数:\(\log{x}\) | |
tan(x) | 三角関数:\(\tan{x}\) | log10(x) | 常用対数:\(\log_{10}{x}\) | |
asin(x) | 逆三角関数:\(\sin^{-1}{x}\) | pow(x,y) | べき乗:\(x^y\) | |
acos(x) | 逆三角関数:\(\cos^{-1}{x}\) | round(x) | 四捨五入 | |
atan(x) | 逆三角関数:\(\tan^{-1}{x}\) | random() | 0.0〜1.0の乱数 |
上のプログラムを参考にして,2つの生物種間の捕食-非捕食関係(植物<動物)を考えた,世代ごとの個体数をグラフ化するプログラムを作ってみましょう.クラス名は「Ex08_3a」とせよ.
ある種のモデルでは,\(n\)世代目の生物種の個体数を\(X(n),Y(n)\)とし,\(X\)が\(Y\)に食べられる関係にあるとすると,\(n+1\)世代目のそれぞれの個体数は次のような式で決まると考えられる.
\[ X(n+1) = X(n) + a_x*X(n) - b_x*X(n)*X(n) - c_x*X(n)*Y(n),\\ Y(n+1) = Y(n) - b_y*Y(n)*Y(n) + c_y*X(n)*Y(n). \]
ここで,係数はそれぞれ,\(a_x\):\(X\)の増加率,\(b_x,b_y\):\(X,Y\)の種族内競争による減少率(環境の悪化は個体数に比例するので,減少率が個体数に比例する),\(c_x\):\(Y\)の捕食による\(x\)の減少率,\(c_y\):\(Y\)の増加率(餌である\(X\)に比例する)である.
世代を横軸,個体数を縦軸として\(X\),\(Y\)それぞれの個体数の変動をプロットしてみましょう.
ヒント:\(X\),\(Y\)それぞれの2つのデータ系列を作成し,1つの折れ線グラフとする(linechart.getData().add(chartseries) のところを linechart.getData().addAll(chartseries1,chartseries2) のようにして系列を追加すれば良い).凡例を表示することにして.「データ系列名 .setName("凡例に表示する文字")」でデータ系列に名前をつけると分かりやすい.