一般に「選択構造」と「反復構造」を組合せて用いることにより,様々な処理をするプログラムを記述することができます.
乱数を用いて,シミュレーションや計算を行う手法をモンテカルロ法と言います.
例えば図形の面積を求める場合,まず図形を含む長方形の内部に非常にたくさんの点をでたらめな位置に取り,全ての点の中で図形に含まれ点の数を調べます.
すると,
(図形の面積)÷(長方形の面積)≈(図形に含まれる点の数)÷(全ての点の数)
という式が成り立つはずなので,点の数の比から図形の面積の近似値を求めることができます.
当然,点の数が多いほど正確な値に近づきます.
ここではモンテカルロ法を使って円周率の値を近似的に求めてみます.
考え方は,(円の面積)=(円周率)X(半径)2 なので,半径1の円の面積を計算すれば円周率を得ることが出来ます.
また,原点を中心とした円を考えた場合,円をまるごと考えるのではなく.(\(x > 0,\ y > 0 \))の部分:円の4分の1(中心角90度の扇型)を考えれば,一辺が1の正方形の領域で考えれば良く,これを4倍すれば円の面積になります.
でたらめな点の座標は, Math.random() によって,0〜1の乱数を発生させて作ります.
/******************************************************* MonteCarlo π *******************************************************/ 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.layout.HBox; import javafx.scene.control.TextField; public class MonteCarlo extends Application{ public static void main(String[] args) { launch(args); } TextField tf = new TextField(); Label label1 = new Label("点の数を入力して下さい."); Label label2 = new Label(""); @Override public void start(Stage stage) throws Exception { stage.setTitle("Application MonteCarlo"); VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10,10,10,10)); root.setSpacing(10.0); HBox pane = new HBox(); pane.setAlignment(Pos.CENTER); pane.setPadding(new Insets(10,10,10,10)); pane.setSpacing(5.0); tf.setAlignment(Pos.CENTER_RIGHT); tf.setMaxWidth(100); pane.getChildren().addAll(label1,tf); tf.setOnAction(event -> calcArea()); root.getChildren().addAll(pane,label2); Scene scene = new Scene(root,300,200); stage.setScene(scene); stage.show(); } //calcAreaの定義:モンテカルロ法で面積を計算 private void calcArea() { String s = tf.getText(); int n = Integer.parseInt(s); //入力した点の数 double x, y; //点の座標の変数 int npi = 0; //図形に含まれる点の数の変数 double area = 0; //面積の変数 for(int i = 0 ; i < n ; i++) { x = Math.random(); //点のx座標 y = Math.random(); //点のy座標 //点が半径1の円(x^2+y^2=1)に含まれるかを判定 if(y <= Math.sqrt(1 - x*x)) { npi++; //円に含まれる場合1増える } } area = 4*(double)npi/n; //4倍して円の面積を求める label2.setText("点の数:" + n + "の時の面積:" + area); } }
以下の問題のどれかのプログラムを作成しなさい.
例題同様様々な図形の面積や体積を求めるのにモンテカルロ法は使用できます.
モンテカルロ法は,積分を求める問題に使用できます.
積分,多重積分の場合,次のように乱数\(x_i\),または乱数の組\((x_i,y_i.z_i)\)を\(n\)個発生させて,それらを被積分関数に代入した値の和を求めることによって近似値が計算できます. \[\int_0^1 f(x)dx \sim \frac{1}{n}\sum_{i=1}^nf(x_i),\quad \int_0^1\int_0^1\int_0^1 f(x,y,z)dxdydz \sim \frac{1}{n}\sum_{i=1}^nf(x_i,y_i,z_i)\]
次の積分を計算する.
モンテカルロ法は,様々な現象の確率を求める問題に使用できます.
例えばサイコロを振る場合,1〜6までの乱数を N 回発生させ,各数値がそれぞれ何回出たかを記録しておけば,(数値 i が出た回数)÷ N で i の出る確率が近似できます.
いくつかの数を順番に並べ替えるプログラムをソートプログラムと言い,様々なやり方があります.
ここでは,バブルソートと呼ばれるプログラムを作ってみましょう.
考え方は,最初にバラバラな順番に並んでいる数列の最後のものから順に1つ前の数と比べて,前のほうが大きければ順番を入れ替えるという操作を繰り返すと最後には一番小さい数が先頭に来ます.その後,同様の操作を2番めの位置まで繰り返すと,2番めに小さい数が2番めの位置に来ます.この操作を最後まで繰り返すと,数列全体が小さい順に並ぶことになります.
/************************************************** Bubble Sort **************************************************/ 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.layout.HBox; import javafx.scene.control.Button; import javafx.scene.control.TextField; public class BubbleSort extends Application{ public static void main(String[] args) { launch(args); } TextField tf = new TextField(); Button btn = new Button("Clear"); Label label1 = new Label("整数を10個入力して下さい."); Label label2 = new Label("1個目:"); Label label3 = new Label(); Label label4 = new Label(); int a[] = new int[10]; //入力を入れる配列変数 int b[] = new int[10]; //結果を入れる配列変数 int i = 1; //入力したデータの数 @Override public void start(Stage stage) throws Exception { stage.setTitle("Application BubbleSort"); VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10,10,10,10)); root.setSpacing(10.0); HBox pane = new HBox(); pane.setAlignment(Pos.CENTER); pane.setPadding(new Insets(10,10,10,10)); pane.setSpacing(5.0); tf.setAlignment(Pos.CENTER_RIGHT); tf.setMaxWidth(50); pane.getChildren().addAll(label2,tf); tf.setOnAction(event -> sortValue()); btn.setOnAction(event -> clrValue()); root.getChildren().addAll(label1,pane,label3,label4,btn); Scene scene = new Scene(root,400,400); stage.setScene(scene); stage.show(); } //clearボタンを押した時の処理 private void clrValue() { //入力データの初期化 for(int n = 0 ; n < a.length ; n++) { a[n] = 0; } i = 1; //入力回数を元に戻す //表示を元に戻す tf.setText(""); label1 = new Label("整数を10個入力して下さい."); label2.setText(i+"個目:"); label3.setText(""); label4.setText(""); } //データを入力した時の処理 private void sortValue() { String s = tf.getText(); tf.setText(""); if(i < a.length){ //9回目の入力まではこちら a[i-1] = Integer.parseInt(s); //入力した値を配列変数に代入 i++; //入力回数のカウンターを1増やす label2.setText(i + "個目:"); } else if(i==a.length){ //10回目の入力の時 a[i-1] = Integer.parseInt(s); i++; //バブルソート for(int j = 0 ; j < b.length ; j++) { b[j] = a[j]; } for(int m = 0 ; m < b.length - 1 ; m++) { for(int k = b.length - 1 ; k > m ; k--) { if(b[k] < b[k-1]) { int tmp = 0; tmp = b[k]; b[k] = b[k-1]; b[k-1] = tmp; } } } //結果の表示 label1.setText("clearボタンを押して下さい."); label3.setText("入力:" + a[0] + "," + a[1] + "," + a[2] +"," + a[3] +"," + a[4] +"," + a[5] +"," + a[6] +"," + a[7] +"," + a[8] +"," + a[9]); label4.setText("結果:" + b[0] + "," + b[1] + "," + b[2] +"," + b[3] +"," + b[4] +"," + b[5] +"," + b[6] +"," + b[7] +"," + b[8] +"," + b[9]); } else { //11回以上入力しようとした時 label1.setText("clearボタンを押して下さい."); } } }
「指定した大きさの魔法陣を作る」,「違う方法のソートプログラム」,「入力した数の最大公約数と最小公倍数を求める」,「入力した数が素数かどうか判定する」,「ニュートン法を使って方程式の解を求める」のどれかのプログラムを作る.考え方は自分で調べてみましょう.クラス名は「Ex09_4」とする.
参考: while文 や for文 の中で break文 を使用すれば,処理の繰り返しを途中で終了できます.