Comments
Description
Transcript
平成27年度 後期 理学部数学科 計算機数学概論 脇先生
11/19 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2015-11-19 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「11/19 の講義・演習」にこの資料を掲載 1 / 15 11/19 JAVA の初歩 5 前回の課題について 今回も良くできていました ただ, 提出数が減りました. また白紙の提出もありました. http://webct.kyushu-u.ac.jp にアクセスできなかったせ いでしょうか? 目標 配列について 配列 変数を番号をつけてまとめたもの ≈ ベクトル 計算機で実現するデータ構造 2 / 15 11/19 配列 例 : 5 教科の平均点を出力するプログラム (Unsued array) public class average{ public static void main(String[] args){ int japaneselang, math, science, socialstudy, english; double ave; japaneselang = 63; math = 94; science = 83; socialstudy = 75; english = 83; ave = japaneselang + math + science + socialstudy + english; ave = (double)ave/5.0; System.out.println("Kokugo = System.out.println("Sugaku = System.out.println("Rika = System.out.println("Shakai = System.out.println("Eigo = System.out.println("Average= "+japaneselang); "+math); "+science); "+socialstudy); "+english); "+ave); } } 3 / 15 11/19 例 : 5 教科の平均点を出力するプログラム (Used array) public class average{ public static void main(String[] args){ int [] points; double ave; points = new int[5]; points[0] = 63; points[1] = 94; points[2] = 83; points[3] = 75; points[4] = 83; ave = (points[0] + points[1] + points[2] + points[3] + points[4] System.out.println("Kokugo = System.out.println("Sugaku = System.out.println("Rika = System.out.println("Shakai = System.out.println("Eigo = System.out.println("Average= "+points[0]); "+points[1]); "+points[2]); "+points[3]); "+points[4]); "+ave); } } 4 / 15 11/19 配列の宣言と確保 int [] points; points = new int[5]; 説明 1 行目 points という名前の int 型の配列を宣言 2 行目 メモリ上に int 型の変数 5 つ確保 注意 int[] points = new int[5]; とまとめてよい 5 は配列の長さ. 0 より小さい値を指定するとコンパイル時に エラーになる java.lang.NegativeArraySizeException 5 / 15 11/19 配列の各要素を参照 points[0] = 63; points[1] = 94; points[2] = 83; points[3] = 75; points[4] = 83; 説明 1–2 行目 points の各要素に値を代入 注意 配列の番号を添え字 (index) と呼ぶ. 添え字は 0 から始まり, 4 (=5-1) でおわり 4 より大きい添え字を指定すると実行時にエラーになる java.lang.ArrayIndexOutOfBoundsException 6 / 15 11/19 配列の宣言と代入 int[] points = {63, 94, 83, 75, 83}; 説明 1 行目 points という int 型の配列を宣言して, 各要素に値 を代入 注意 配列に代入するために, プログラムの途中で points = {63, 94, 83, 75, 83}; はできない 7 / 15 11/19 配列の各要素に値を代入 ave = (points[0] + points[1] System.out.println("Kokugo = System.out.println("Sugaku = System.out.println("Rika = System.out.println("Shakai = System.out.println("Eigo = + points[2] + points[3] + p "+points[0]); "+points[1]); "+points[2]); "+points[3]); "+points[4]); 説明 1 行目 points の各要素から平均を計算 2–6 行目 points の各要素を出力 注意 points[0] で配列 points の 1 番目の要素が参照できる points[3] で配列 points の 4 番目の要素が参照できる 8 / 15 11/19 for 文を使ってもっと簡単に書ける int sum = 0; for(int i=0; i<5; i++){ sum = sum + points[i]; } ave = (double)sum/5.0; 説明 2–4 行目 配列 points の各要素を足し算 5 行目 int 型の変数 sum を double 型に変換して割り算 注意 添え字を変数として for 文で計算している. これはよく使う! 配列の長さは, points.length と配列の名前.length とする と参照できる. ave = (double)sum/points.length; の場合は, 型変換は 必須 9 / 15 11/19 課題 1 : 正の整数 N をキーボードから入力して, 以下を満たす配 列 array を作成する: 長さ N, 各要素には, 1 から 6 までの擬似乱数で生成される整数値が 代入されている. この配列 array に対して, 以下を出力するプログラムを提出しな さい. ファイル名は statistics.java としなさい. 配列 array の全要素 配列 array の標本平均 E , すなわち N 1 ∑ E= array[k] N k=1 配列 array の標本分散 S, すなわち 1 ∑ (array[k] − E )2 N −1 N S= k=1 10 / 15 11/19 課題 1 の出力例 $ java statistics Input a positive integer :10 array[0] = 2 array[1] = 1 (中略) array[8] = 5 array[9] = 3 E = 3.1 S = 2.7666666666666666 11 / 15 11/19 課題 2 : 正の整数 N をキーボードから入力して, 以下を満たす配 列 array を作成する: 長さ N, 各要素には, 1 から 5N までの擬似乱数で生成される整数値が 代入されている. この配列 array に対して, 以下を出力するプログラムを提出しな さい. ファイル名は findmax.java としなさい. 配列 array の全要素 配列 array の中で一番値の大きい要素の添字 index とその 要素の値 array[index] 12 / 15 11/19 課題 2 の出力例 $ java findmax Input a positive integer :10 array[0] = 30 array[1] = 2 (中略) array[7] = 49 array[8] = 34 array[9] = 13 Max : array[7] = 49 13 / 15 11/19 課題 2 のヒント 1 : S = {a1 , . . . , am } に対して m = max S を求 めたい. まず次に気づくこと: 数列 mk (k = 1, . . . , n) を次のように定める: m1 = a1 , mk+1 = max{mk , ak+1 } (k = 1, . . . , n − 1) (1) この時, mn = m である 2 . 次に (1) を JAVA 言語でどう実現するか考える. 最大値を変数 int max に格納する. わざわざ mk のための配 列を作らないで mk の値を max に代入する. m1 = a1 は, max = array[0] に対応 max{mk , ak+1 } は二つの要素の大きさを比較. もし max > array[k] なら最大値は max, そうでなければ, ... (k = 1, . . . , n − 1) とあるから... 1 2 参考にしないほうがよいかも mk が {a1 , . . . , ak } の最大値であることを数学的帰納法で示せばよい. 14 / 15 11/19 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「11/19 の課題」をクリック 期限は, 11/19 PM 11:59 です 15 / 15 11/19 11/26 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2015-11-26 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「11/26 の講義・演習」にこの資料を掲載 1 / 15 11/19 11/26 findmax について 最大値を取る添字を見つける I for(int k=0; k<N; k++){ max = Math.max(max,array[k]); } for(int j=0; j<N; j++){ if(array[j] == max){ J = j; } } コメント N 回の比較で済むところを 2N 回比較しているので, もう少 し賢い方法を使いましょう Math.max を使うと添字まで見つけられません 2 / 15 11/19 11/26 最大値を取る添字を見つける II for(int i=0; i<N; i++){ if(max>points[i]){ max = max; }else{ max = points[i]; } } int x = 0; for(int i = 0; i<N; i++){ if(points[i]==max){ x=i; } } コメント これも, 無駄な比較を N 回やっています if 文において, 何もしないところは書かなくても構いません 3 / 15 11/19 11/26 最大値を取る添字を見つける III for(int i=0; i<N; i++){ B = true; for(int k=0; k<N; k++){ if (points[i]<points[k]){ B = false; } } if (B){ System.out.println("Max : Array["+i+"] = "+points[i]); } } コメント N 2 回の比較で最大値を探している 多分, 添字を見つけることができなかったので, 無駄な比較を して添字まで探している 4 / 15 11/19 11/26 最大値を取る添字を全て見つける int [] array = new int [N]; int [] maxiarray; int max = 0; int j; maxiarray = new int [N]; j = 0; for(int i=0; i<N; i++){ array[i] = (int)(Math.random()*5*N)+1; System.out.println("array["+i+"] = "+array[i]); if(max < array[i]){ max = array[i]; maxiarray = new int [N]; maxiarray[0] = i; j = 0; } else if(max == array[i]){ j = j+1; maxiarray[j] = i; } } コメント 最大を取る添字が複数個ある場合を想定したプログラム 5 / 15 11/19 11/26 JAVA の初歩 6 目標 二次元配列について 二次元配列 行列のようなもの 一次元配列の復習 int [] array = new int [N]; は array[0] array[1] array[2] array[3] int int int int ... array[N-1] int 6 / 15 11/19 11/26 二次元配列 例 : 2 × 3 の行列の各要素の総和を計算 public class sumM{ public static void main(String[] args){ int [][] matrix = new int[2][3]; matrix[0][0] = 1; matrix[0][1] = -3; matrix[0][2] = 2; matrix[1][0] = 4; matrix[1][1] = 1; matrix[1][2] = -1; for(int i=0; i<2; i++){ for(int j=0; j<3; j++){ System.out.println("matrix["+i+"]["+j+"]="+matrix[i][j]) } } int sum = 0; for(int i=0; i<2; i++){ for(int j=0; j<3; j++){ sum = sum + matrix[i][j]; } } System.out.println("sum = "+sum); } 7 / 15 } 11/19 11/26 配列の宣言と確保 int [][] matrix = new int[2][3]; 説明 1 行目 2 × 3 の二次元配列 matrix を宣言. 各要素は整数型 注意 二次元配列は行列に相当する. この場合, ( ) m11 m12 m13 M= , where mij ∈ Z m21 m22 m23 に相当 ただし, 添字の始まりは 0 であることに注意 8 / 15 11/19 11/26 配列の各要素を参照 matrix[0][0] = 1; matrix[0][1] = -3; matrix[0][2] = 2; matrix[1][0] = 4; matrix[1][1] = 1; matrix[1][2] = -1; 説明 1–2 行目 matrix の各要素に値を代入 注意 次のように宣言と代入をまとめても良い int [][] matrix = { { 1, -3, 2}, { 4, 1, -1}, }; 上記で書く場合は, {} の対応に注意すること 配列同様, プログラムの途中で, matrix = { { 1, -3, 2}, { 4, 1, -1}, }; とはできない. 9 / 15 11/19 11/26 配列の各要素に値を代入 for(int i=0; i<2; i++){ for(int j=0; j<3; j++){ System.out.println("matrix["+i+"]["+j+"]="+matri } } int sum = 0; for(int i=0; i<2; i++){ for(int j=0; j<3; j++){ sum = sum + matrix[i][j]; } } System.out.println("sum = "+sum); 説明 1–5 行目 matrix の各要素を出力 6–12 行目 matrix の各要素を総和を計算 注意 二重 for 文を使っていることに注意 (i と j) の有効範囲にも 注目) 10 / 15 11/19 11/26 課題 1 : 正の整数 N をキーボードから入力して, 以下を満たす二 次元配列 matrix を作成する: 長さ N × N, 各要素には, −1 以上 1 未満の擬似乱数で生成される浮動小数 点数 1 が代入されている. この配列 matrix に対して, 以下を出力するプログラムを提出しな さい. ファイル名は compmat.java としなさい. 二次元配列配列 matrix の全要素 二次元配列配列 matrix を行列 M = (mij )1≤i,j≤N とみなして, n1 = N ∑ N ∑ i=1 j=1 v u N N u∑ ∑ |mij | and n2 = t mij2 i=1 j=1 ヒント Math.abs が絶対値, Math.sqrt が平方根 1 double のこと 11 / 15 11/19 11/26 課題 2 の出力例 $ java compmat Input a positive integer :3 matrix[0][0]=0.34064437644708856 matrix[0][1]=-0.43628759302949827 matrix[0][2]=0.5066400548104204 matrix[1][0]=0.3225752629191847 matrix[1][1]=-0.3144773090430606 matrix[1][2]=-0.8903180980737053 matrix[2][0]=0.31829218960236005 matrix[2][1]=-0.6441572598720473 matrix[2][2]=0.0683225030029877 v1 = 1.285024712393592 v2 = 0.7875933555173698 12 / 15 11/19 11/26 課題 2 : 正の整数 N をキーボードから入力して, 以下を満たす二 次元配列 matrix を作成する: 長さ N × N, 各要素には, −5 から 4 までの擬似乱数 2 で生成される整数値 が代入されている. この配列 matrix に対して, 以下を出力するプログラムを提出しな さい. ファイル名は mulmat.java としなさい. 二次元配列配列 matrix の全要素 二次元配列配列 matrix を行列 M とみなして, 行列積 M × M T . ここで M T は行列 M の転置行列である. ヒント 行列積 M × M T を要素ごとに書き下すと, for 文をどう使え ば良いか見えるはず 2 ちょっと細かいけれど Math.random() で生成されるのは, 0 以上 1未満 の 浮動小数点数だから 13 / 15 11/19 11/26 課題 2 の出力例 $ java mulmat Input a positive integer :3 matrix[0][0]=-3 matrix[0][1]=-2 matrix[0][2]=-2 matrix[1][0]=3 matrix[1][1]=2 matrix[1][2]=-1 matrix[2][0]=2 matrix[2][1]=0 matrix[2][2]=3 mmt[0][0]=17 mmt[0][1]=-11 mmt[0][2]=-12 (中略) mmt[2][1]=3 mmt[2][2]=13 14 / 15 11/19 11/26 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「11/26 の課題」をクリック 期限は, 11/26 PM 11:59 です 15 / 15 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2015-12-03 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「12/3 の講義・演習」にこの資料を掲載 1/1 11/26 の課題について M × M T の計算 : K = MM T として, K の各要素を kij , M の各 要素を mij とおくと kij = n ∑ mik mjk (1 ≤ i, j, ≤ n) k=1 である. これを各 i, j についけ計算するだけ M × MT mmt[i][j] = 0; for(int k=0; k<N; k++){ mmt[i][j] = mmt[i][j] + matrix[i][k]*matrix[j][k]; } 2/1 11/26 の課題について その他 while 文使っている人がいましたが, サイズが決まっているの で for 文が良いかも Math.random から整数値や [0, 1) 以外の浮動小数点数を生成 するのに手間取っている人がいましたが, int matrix[i][j] = (int)(10*Math.random()-5); や double matrix[i][j] = 2*math.random()-1; で十分です. 3/1 JAVA の初歩 7 目標 メソッドについて メソッド 関数・写像のようなもの 大切なのは, 「おまじない」と定義域・値域 プログラムの中で書いた手続きの共通項を見つけ出して, 抽 象化してプログラムを簡素にするために必須となる知識 「おまじない」さえわかれば, 簡単に使いこなせるはず 4/1 メソッド 例 : 身長, 体重から BMI を計算する double height = 1.70, weight = 65.0; double bmi = weight/(height*height); System.out.println("Your BMI = "+bmi); if(bmi > 21.0){ System.out.println("Your BMI is bigger than the standard."); }else if(bmi < 17.0){ System.out.println("Your BMI is smaller than the standard."); }else{ System.out.println("Your BMI is in standard."); } コメント BMI の計算は, f (height, weight) = bmi という関数とみな せる そのあとの手続きも, bmi の値を代入すれば実行できる 5/1 メソッドの例 1 public class bmi{ public static double ComputeBMI(double height, double weight){ double bmi = weight/(height*height); return bmi; } public static void main(String[] args){ double height = 1.70, weight = 65.0; double bmi = ComputeBMI(height, weight); } } コメント 2 行目 ∼ 5 行目 与えられた height と weight をが与えられると, bmi を求めるメソッド ComputeBMI の定義 8 行目 ComputeBMI を利用した計算 6/1 メソッドの書き方 public static (返り値) (関数名)(引数 1, 引数 2, ...){ (手続き) return ...; } 戻り値 double の値を返すメソッドなら double と書く. int の値を返すなら int と書く. また, メソッドの最 後は, return を書いて何を返すか明示する 関数名 半角英数字で記述する. f とか g など一文字よりも, ある程度何をするメソッドなのか分かる名前を書く と良い 引数 引数を書くときは, 変数の型と変数を合わせて書く こと 7/1 例 1 : f : R × R → R, (x, y ) → z public static double examplef(double x, double y){ double z; (手続き) return z; } 例 2 : g : R × Z → Z, (x, y ) → z public static int exampleg(double x, int y){ int z; (手続き) return z; } 例 3 : 入力 double x に対して, x の値出力するメソッド public static void exampleh(double x){ System.out.println("x = "+x); } 何も返さない場合は返り値を void とし, return を書かない 8/1 例 4 : 引数として配列も O.K. public static double exampleI(double [] vector){ double z = 0.0; for(int i=0; i<vector.length; i++){ z = z + vector[i]; } return (z/vector.length); } 例 5 : 10/29 の課題 : 円周率の近似 public static double Machin(){ double m = 4*Math.atan(1.0/5.0) - Math.atan(1.0/239.0); return (4*m); } 9/1 例 6 : bmi の後半 public static void message(double bmi){ System.out.println("Your BMI = "+bmi); if(bmi > 21.0){ System.out.println("Your BMI is bigger than the standard. }else if(bmi < 17.0){ System.out.println("Your BMI is smaller than the standard }else{ System.out.println("Your BMI is in standard."); } } 10 / 1 課題 1 : (おみくじ) 10 回おみくじを引くプログラムを作成した い. そのために下記のプログラムを作成した. この中でおみくじ の出力 1 をメソッド message として記述して, プログラムを完成 させ提出しなさい. 課題 1 の主要部分 public class Omikuji10{ public static void main(String[] args){ double d; for(int i=0; i<10; i++){ d = Math.random(); message(d); } } } なお, 以下の条件を満たすこと: d の値は出力させなくて良い 2 ファイル名は Omikuji10.java とすること 1 2 10/22 の課題 1 に記載している 10/22 の課題 1 と異なる 11 / 1 課題 2 : (Collatz の問題) 正の整数 n に対して次の操作を適用 する: (操作) : n が偶数なら n/2 とする. n が奇数なら 3n + 1 とする 与えられた n に対して (操作) を繰り返し適用すると 5 × 260 まで の n であれば必ず 1 になることが知られている 3 . 与えられた n に対して, 初めて 1 になるまでに要した (操作) の回 数を f (n) とする. キーボードから入力された n に対して f (n) を出 力するプログラムを提出しなさい. なお以下の条件を満たすこと: 「入力された n に対して, f (n) を戻り値とする」メソッドを 作ること. メソッド名はなんでも良い ファイル名は, collatz.java とすること (必須ではないが, 興味があれば)n = 1 ∼ 105 までの様々な n に対して試すこと 3 https://en.wikipedia.org/wiki/Collatz_conjecture 12 / 1 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「12/3 の課題」をクリック 期限は, 12/3 PM 11:59 です 13 / 1 12/3 12/10 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2015-12-10 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「12/10 の講義・演習」にこの資料を掲載 1 / 13 12/3 12/10 12/3 の課題について コメント : 返り値があるメソッドなら, main でも返り値を受け 取る変数を用意する あまり良くない例 public static int f(int n){ int N = 0; (中略) return N; } public static void main(String[] args){ (中略) f(n); } 8 行目は int count = f(n); とした方が良い. 2 / 13 12/3 12/10 コメント (続き) : メソッドで利用する変数と main で利用する 変数の関係 何が出力されるでしょう? 5 でしょうか 15 でしょうか? public static void function(int n){ n = n + 10; } public static void main(String[] args){ int n = 5; function(n); System.out.println("n="+n); } メソッドで利用される変数はメソッドの中だけで有効 なお「main から function を呼び出す」という 3 / 13 12/3 12/10 JAVA の初歩 8 目標 ファイルの入出力について 数値実験などに必須となる技術 「こんなこともできる」ということを知る・経験することが 大切 JAVA だけでなくたいていのプログラム言語で利用出来る csv ファイル カンマで区切られた表 1 つの行 (レコード) に, 1 つ以上の要素 (フィールド) がある 各レコードは, 同じ数のフィールドで構成される 4 / 13 12/3 12/10 ファイルからの入力 例 : 10 回の課題 (1 回 10 点満点) から各学生の講義に対する評 定を決めたい 初めの列は名前, 次の列からは, 各回の課題の点数 評定は 90 点以上 (A), 80 点以上 90 点未満 (B), 70 点以上 80 点未満 (C), 60 点以上 70 点未満 (D), 60 点未満 (F) 10 回の課題の各点数をまとめたファイル : data.csv Student1,9,10,2,10,7,1,3,6,10,10 Student2,2,10,10,5,9,2,5,10,8,10 Student3,7,1,9,10,7,8,8,4,7,2 ...(中略)... Student98,1,10,7,10,2,10,8,6,5,3 Student99,8,3,1,8,7,8,7,5,4,9 Student100,4,9,8,9,6,7,10,5,1,9 注意 学生に対応する行の点の合計が, その学生の合計点 例えば, Student2 の合計点は 71 点で C 5 / 13 12/3 12/10 getGrade.java (これは web からダウンロードできる) public class getGrade{ public static void main(String [] args){ int M = 100; int N = 11; String [][] stringArray = new String[M][N]; int [][] points = new int[M][N-1]; String [] names = new String[M]; readFile(stringArray); separateP(stringArray, points); separateS(stringArray, names); } (中略) } コメント : csv ファイルの読み込み部分 readFile はこちらで作 成済み 8 行目 二次元配列 points には学生の各課題の成績が記録. points[i][j] は (i+1) 番目の学生の (j+1) 回目の 課題の成績が記録. 配列の長さは 100×10 9 行目 配列 names には学生の名前を記録. names[i] は (i+1) 番目の学生の名前が記録. 配列の長さは 100 6 / 13 12/3 12/10 課題 1 : getGrade.java にある points を使って評定 A, B, C, D, F の数を出力するプログラムを完成させ提出しなさい. なお, (意味のある) メソッドを作ることを推奨するが, 作らなくて もよい. 余裕があれば, 最高得点やその学生, 最低得点やその学生, 平 均点などを出力させるなど, いろいろと工夫しなさい. data.csv は改変してはいけない. getGrade.java は課題のページに掲載している. 出力例 (ただし, 数はデタラメ) A : 10 B : 30 C : 20 D : 10 F : 5 Max : 100 (Student73) Min : 15 (Student4) Average : 55.5 7 / 13 12/3 12/10 ファイルへ出力 例 : (Collatz の続き) 前回の課題で入力された n に対して 1 にな るための (操作) の回数 F (n) を求めるプログラム collatz.java を作成した. n = 1 ∼ 1000 に対してこの F (n) を collatz.csv と いうファイルに書き出すプログラム collatz2.java を作成し たい. collatz2.java というプログラムでは, collatz.csv を書き 出すメソッドを用意した. csv ファイルを書き出すメソッド void writeVals(int [] array) array に n = 1 から n = 1000 までの F (n) を記録させると writeVals(array); で collatz.csv が作成できる. 8 / 13 12/3 12/10 課題 2 : 課題のページに掲載している collatz2.java を完成さ せて提出しなさい. さらに, gnuplot を使って 1 , 横軸 n 縦軸 F (n) となるグラフをプロットして, collatz.png というファイルを作 成して提出しなさい. 余力があれば : n = 1 から n = 1000 までの G (n) = “n が 1 になるまでのに生成される数の中での最大数” と定めて, 課題 2 と同様に横軸 n 縦軸 G (n) のグラフを作成しなさ い. 例えば, 10 → 5 → 16 → 8 → 4 → 2 → 1 ∴ G (10) = 16 となる. また H(k) = “F (n) = k となる n の個数” に対して課題 2 と同様に横軸 n 縦軸 H(n) のグラフを作成するの も面白いかもしれない. 1 次のページを参照 9 / 13 12/3 12/10 Gnuplot の使い方 使い方の詳細は, 「Gnuplot」,「gnuplot」などで検索する csv をグラフに表示 1 ターミナルで, 「gnuplot」とタイプ 2 「set datafile separator ","」 とタイプ 3 「plot "collatz.csv" w lp」とタイプすると, グラフが 表示 グラフを collatz.png で保存 「set terminal png 」とタイプ 2 「set output "collatz.png"」 とタイプ 3 「plot "collatz.csv" w lp」とタイプすると, collatz.png が生成される 4 再度「set output」とすると, グラフを表示する なお, gnuplot を終了するには「quit」 1 10 / 13 12/3 12/10 注意 1 : gnuplot を起動すると, 以下の画面になるかもしれない が, 「キャンセル」を押してください. 11 / 13 12/3 12/10 注意 2 : 私有の Mac には gnuplot が入っていないと思うので, 各自検 索してインストールしてください. 難しいと思うので, iMac にログインして作業すると良いと思 います. 12 / 13 12/3 12/10 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「12/10 の課題」をクリック 期限は, 12/10 PM 11:59 です 13 / 13 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2015-12-17 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「12/17 の講義・演習」にこの資料を掲載 1/1 12/10 の課題について {{\tt getGrade.java}} int M = 100; int N = 11; String [][] stringArray = new String[M][N]; int [][] points = new int[M][N-1];; String [] names = new String[M]; (中略) int[] sum = new int[100]; for(int i=0; i<100; i++){ for(int j=0; j<10; j++){ sum[i] = sum[i] + points[i][j]; } } (以下略) コメント i<100 や j<10 は i<M や j<N-1 などと書いた方が間違いが少 ない 2/1 無限ループするプログラム int m = 0; for(int n=1; n<1000; n++){ m = 0; while(n != 1){ if(n % 2 == 0){ n = n/2; }else{ n = 2*n + 1; } m = m + 1; } } 無限ループする理由 for 文にある n が while 文の中で最終的に n= 1 になる n++しても n= 2 で for 文で n= 1 になる これの繰り返し=無限ループ 3/1 getGrade.java の結果 A : 0 B : 0 C : 4 D : 24 F : 72 MAX : Student61 : 76 MIN : Student60 : 33 Ave : 53.78 4/1 Figure : F (n) のプロット図 5/1 Figure : G (n) のプロット図 6/1 再帰的アルゴリズム 目標 再帰的アルゴリズムを知り, 簡単なものを実装する メソッドを利用 数学的帰納法と同じ考え JAVA だけでなくたいていのプログラム言語で利用出来る 再帰 (さいき) アルゴリズム 手続きの記述の中で, それ自身への参照があらわれる手続き 再帰呼び出し, 再帰プログラミングなどと呼ばれる技法 7/1 階乗の計算 例 1 : n! = n · (n − 1) · (n − 2) · · · 2 · 1 の · · · を丁寧に書くと { 1 (n = 1) n! = n · (n − 1)! (n > 1) となる この定義は再帰的になっている 注目すべき点は, 定義自身だけでなく終了する条件もある 階乗 n! を計算する再帰的アルゴリズム public static int factorial(int n){ int val; if(n == 1){ val = 1; }else{ val = n * factorial(n-1); } return val; } 8/1 フィボナッチ数列 例2 : { F (n) = 1 (n = 1, 2) F (n − 1) + F (n − 2) (n ≥ 3) この定義も再帰的になっている & 定義自身だけでなく終了 する条件もある フィボナッチ数列 F (n) を計算する再帰的アルゴリズム public static int fib(int n){ int val; if(n == 1 || n == 2){ val = 1; }else{ val = fib(n-1) + fib(n-2); } return val; } 9/1 fibonacci.java ダウンロードして, 27 行目の int N = 5; の数字を N = 10 ∼ 45 に変えて自分で動かしなさい fibonacci.java には, 再帰を使う計算と使わない計算の 2 種類のメソッドが実装されている N = 45 くらいにすると 2 つのメソッドの違いがわかる 2 つのメソッドの違い fib(n) は再帰を使う fib2(n) は再帰を使わない 計算時間に差が出る. fib2(n) の方が速い なぜでしょう? 10 / 1 理由 : 2 つのメソッドで要する足し算の回数の違い fib2(n) では, n − 2 回 ∴ 計 43 回 fib(n) では, G (n) 回かかるとすると, G (n) = G (n − 1) + G (n − 2) + 1 ⇒ (G (n) + 1) = (G (n − 1) + 1) + (G (n − 2) + 1) ∴ G (n) + 1 自身がフィボナッチ数列である F (45) = 1134903170 なので, fib2(n) と比べ物にならないほ ど足し算をたくさんする 11 / 1 計算量 (あるいは, 計算の複雑さ) : ざっくりとして定義 計算機の数理モデルにおいて, 入力に対して手続きが要する 最大ステップ数 ここでは, 入力が n 手続きがフィボナッチ数列を求めるメソッド 最大ステップ数が足し算の回数 に対応 参考書 : アルゴリズムや計算量に興味があれば次の書籍を推薦 アルゴリズムイントロダクション 計算理論の基礎 The Art of Computer Programming 12 / 1 再帰を使う時・使わない時 再帰を使う 再帰を使うと簡単に書ける場合 再帰を使わない 再帰を使うと計算時間が膨大になることがある 再帰を使わなくても書ける場合 13 / 1 課題 1 : (組み合わせの数) 非負の整数 n, k に対して次のように 定義する: ( ) ( )1( ) (n = k, k = 0) n n−1 n−1 = + (それ以外) k k −1 k ( ) ( ) 入力した n に対して, n0 から nn を全て列挙するプログラムを提 出しなさい. なお以下の条件を満たすこと: (n ) k を再帰的アルゴリズムで求めること ファイル名は combination.java とすること もし, 余裕があれば, 再帰を使わないアルゴリズムと比べてみると 良い. 14 / 1 課題 2 : (Euclid の互除法) 二つの自然数 a, b (a ≥ b) の最大公約 数 GCD(a, b) を求める. なお, GCD(a, 0) = a と定める. a を b で 割った時の余りを r とおくと, GCD(a, b) = GCD(b, r ) が成り立つ. Euclid の互除法を再帰を使うメソッドと再帰を使わ ないメソッドの二つをプログラムして euclidean.java として提 出しなさい. また, GCD(1826, 1742), GCD(783049, 152963), GCD(1134903170, 701408733) を求めてコメント欄に書きな さい, 15 / 1 課題 2 の続き : Euclid の互除法の手続きは以下のとおり: 1 入力 a, b (a ≥ b) 2 もし b = 0 ならば, a を最大公約数として出力 3 b ̸= 0 であれば, a ← b, b ← r (ただし r は a を b で割った時 の余り) として, 2 に戻る. 注意 : ペア a, b は左くる数字が必ず右にくる数字より大きいか 等しい 16 / 1 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「12/17 の課題」をクリック 期限は, 12/17 PM 11:59 です 17 / 1 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2016-01-07 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「1/7 の講義・演習」にこの資料を掲載 1/1 12/17 の課題について euclidean.java : 非再帰アルゴリズムで苦戦していたようで すが, 次のように書くと正しく動きます 非再帰なユークリッドの互除法 public static int gcd1(int a, int b){ int tmp; while(b != 0){ /* a を b に, b を a%b に入れ替える */ tmp = a % b; a = b; b = tmp; } return a; } 2/1 アルゴリズムの実装 目標 アルゴリズムを考えて JAVA で実装する アルゴリズムと擬似コード 解決したい問題からいきなりコンピュータ言語で実装して計 算することは難しい 問題の理解して解決するためのアルゴリズムを考える アルゴリズムをコンピュータ言語で実現するためには, ま ず擬似コードで記述する 擬似コードで書けると (簡単な) アルゴリズムはコンピュータ 言語で実装しやすくなる さらにアルゴリズムの解析 (最悪どれくらい計算コストがか かるかなど) もできる 3/1 ソート (整列) 問題 : N 個の整数を小さい順に並び替える 考え方 : コンピュータ言語で実現するためには, 入力と出力を定義し なければならない 入力 : N 個の整数 a1 , . . . , aN 出力 : ai1 ≤ ai2 ≤ · · · ≤ aiN となるように a1 , . . . , aN を並び替える 例 : a1 = 5, a2 = 3, a3 = 6, a4 = 7, a5 = 1 ならば, a5 ≤ a2 ≤ a1 ≤ a3 ≤ a4 の順に出力する 要素の並び替えることをソート (整列) するという 4/1 ソートのアルゴリズム : たくさんある 挿入ソート バブルソート (単純交換法) マージソート クイックソートなど 挿入ソート : これでソートできることの証明や計算量の解析は しない a[1] から a[k-1] までがソートされていると仮定する. a[k] を a[1] から a[k] までソートされるように挿入する これで a[1] から a[k] までソートされている これを k=2 から N まで繰り返す 5/1 挿入ソートの例 1 5 3 6 7 1 「ソート済みの要素に下線」 2 5 3 6 7 1 → 3 5 6 7 1 「3 の位置を探す」 3 3 5 6 7 1 → 3 5 6 7 1 「6 の位置を探す」 4 3 5 6 7 1 → 3 5 6 7 1 「7 の位置を探す」 5 3 5 6 7 1 → 1 3 5 6 7 「1 の位置を探す」 6 1 3 5 6 7 「終了」 InsertionSort の擬似コード? for k = 2 to N /* a[1] から a[k-1] までソート済み */ a[1] から a[k] が小さい順に並ぶように a[k] を挿入する; end コメント : これではまだ不十分. もう少し詳しく書く必要がある 6/1 InsertionSort の擬似コード for k = 2 to N key = a[k]; i = k-1; while i > 0 かつ a[i] > key a[i+1] = a[i]; i = i - 1; end a[i+1] = key; end 挿入ソートの例 (最後の反復) 3 5 6 7 1 → 1 3 5 6 7 「1 の位置を探す」 擬似コードでは k=5, key = a[5] = 1, i = 4 i > 0 かつ a[4] > key なので, a[5] = 7, i = 3 となる i > 0 かつ a[3] > key なので, a[4] = 6, i = 2 となる i > 0 かつ a[2] > key なので, a[3] = 5, i = 1 となる i > 0 かつ a[1] > key なので, a[2] = 3, i = 0 となる while を抜ける. a[1] = 1 となる 7/1 バブルソート : これでソートできることの証明や計算量の解析 はしない 1 番目の要素と 2 番目の要素を比較して, 大小関係が逆であ れば交換する 2 番目の要素と 3 番目の要素を比較して, 大小関係が逆であ れば交換する これを (N − 1) 番目と N 番目まで行う. これで N 番目の要素 は一番大きい要素になる 次に同じことを (N − 1) 番目の要素まで行う. これで (N − 1) 番目の要素は二番目に大きい要素になる これを繰り返すと, 1 番目から N 番目まで小さい順に並ぶ 8/1 バブルソートの例 1 53671 2 53671→35671 3 35671 4 35671 5 3 5 6 7 1 → 3 5 6 1 7 「一番目に大きい 7 が a5 」 6 35617 7 35617 8 3 5 6 1 7 → 3 5 1 6 7「二番目に大きい 6 が a4 」 9 35167 10 3 5 1 6 7 → 3 1 5 6 7「三番目に大きい 5 が a3 」 11 35167 12 3 1 5 6 7「四番目に大きい 3a2 」 13 1 3 5 6 7 「終了」 9/1 バブルソート (擬似コード) : bubbleSort for i = 1 to N-1 for j = 1 to N - i if a[j] > a[j+1] swap(a[j], a[j+1]); /* a[j] と a[j+1] を交換 */ end end end 注意 : swap は自分で考えましょう 擬似コードはアルゴリズムを理解しやすくしたもの JAVA や, C 言語, C++に似ているが必ずしも同じではない. 例えば配列の添字に注意 10 / 1 課題 : sort.java というファイルを作って, その中で挿入ソー トとバブルソートを記述して提出しなさい. 数列 {an }N n=1 は整数型の配列で 1 から 1000 までの乱数で生 成しなさい N を 200, 400, 600, 800, 1000 として, 挿入ソートやバブル ソートでどれくらい時間がかかるか計測して, コメント欄に 書きなさい 時間の計測の仕方 一番簡単なのは, time コマンドの利用 (user time) time java sort で, 出力される user time がプログラム自 体の実行にかかった時間 11 / 1 時間の計測の仕方 (続き) 1/1000 秒の測り方 long start = System.nanoTime(); (手続き) long end = System.nanoTime(); System.out.println((end - start) / 1000000f + "ms"); これで (手続き) の要する時間を計測できる コメント : 実は 1/1000 秒まで正確に計測しなくても良い 12 / 1 配列に関する注意 : 次のコードでは array の値は何になるで しょうか? public static void nothing(int [] array){ array[0] = 35; array[1] = 45; } public static void main(String [] args){ int [] array = new int [2]; array[0] = 1; array[1] = 2; System.out.println("array[0] = "+array[0]); System.out.println("array[1] = "+array[1]); nothing(array); System.out.println("array[0] = "+array[0]); System.out.println("array[1] = "+array[1]); } 13 / 1 配列に関する注意の続き : 出力は以下の通りである array[0] array[1] array[0] array[1] = = = = 1 2 35 45 コメント : 配列の場合, メソッド内での変更はメソッドが終了しても 残ってしまう 乱数で発生させたソート前の配列は複製しておいたほうが よい 14 / 1 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「1/7 の課題」をクリック 期限は, 1/7 PM 11:59 です 15 / 1 1/7 1/21 計算機数学概論 脇 隼人 九州大学 マス・フォア・インダストリ研究所 2016-01-21·28 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「1/21 の講義・演習」にこの資料を掲載 1 / 16 1/7 1/21 アルゴリズムの実装 目標 アルゴリズムを考えて今までの知識を使って JAVA で実装 する プログラムを書いてシミュレーションすることで, 問題の背 後にある数理を知る 構成論的な証明だとプログラムで実現して確認することがで きることもある, ことを知る テーマ : 今回と 1/28 の時間を使って課題に取り組む 確率論, 数値解析, 整数問題などが題材 アルゴリズムが複雑だと思った時は, ノートに擬似コードを 一度書いてみる 興味を持った課題については各自掘り下げてみる 2 / 16 1/7 1/21 課題 1 : コインを投げて表ならば 1, 裏ならば 0 としてこの操作 を繰り返して記録する. 初めて 0, 1 のパターン a = a1 a2 . . . ak が 出る時間 Na とする. 例えば 01001010101100010101110 . . . で, あれば N00 = 4, N10 = 3, N101 = 7 である. 表の出る確率を 1/2 裏の出る確率を 1/2 として, a = 00, 01, 10, 11 の 4 パターンの 期待値を計算したい. ここでは, それをシミュレーションで近似的 に求めることにする. ファイル名は binsequence.java として提 出しなさい. 1 回の試行でコインを投げる回数 1000 とする これを 10000 回実行する 各回で Na を求めて平均を出力する (余力があれば) 表が出る確率を変えたり, a の列を変えて計 算しなさい 3 / 16 1/7 1/21 ∫ b f (x) dx を台形の面積の和で近似 課題 2 : (複合台形則) S := a y y = f (x) f (xj−1 ) f (xj ) T a = x0 x1 xj−1 xj xn−1 b = xn h = (b − a)/n として, xj = a + jh (j = 0, . . . , n) 台形 T の面積 (f (xj−1 ) + f (xj ))h 台形 T の面積 = 2 台形を全て足す n ∑ (f (xj−1 ) + f (xj ))h S(n) := 2 j=1 x 4 / 16 1/7 1/21 課題 2 の続き : 以下の二つの積分を複合台形則を利用して値を 求めなさい. ファイル名は trapezoid.java にして提出すること. 以下の二つの積分を求めなさい: ∫ 1 (1) S1 = 0 ∫ (2) 1 dx, 1+x 1 e πx sin(πx) dx S2 = 0 n = 2k として, k = 1, . . . , 10 に対する誤差 |S − S(n)| を全て 出力させなさい 1 . f (x) の関数値の計算をメソッドとして用意するとプログラム が楽になるかも 1 余力があれば, n を変化させると誤差がどれくらい変化するか推測してコメ ント欄に記入しなさい 5 / 16 1/7 1/21 課題 3 : (行列の掛け算) 下記に従って n × n 行列 A, B の掛け算 を行うプログラムを提出しなさい. ファイル名は matmul.java と すること. A, B の各要素は-1 以上 1 未満の擬似乱数で生成される浮動 小数点数とする 積 AB を計算するのにかかる時間を出力しなさい 2 2 余力があれば, n を 100, 500, 1000, 1500, 2000 と変化させると計測時間がど のように変化するかコメント欄に記入しなさい 6 / 16 1/7 1/21 課題 4 : (不思議な 4 桁) 入力 N を 4 桁の正の整数とする. 操作 g は「N の 4 桁を並び替えてできる最大数と最小数の差を返す 」 とする. この操作を N → g (N) → g (g (N)) → g (g (g (N))) → · · · と繰り返すとどうなるか確認するプログラムを書き提出しなさい. ファイル名は, gcomp.java とすること. また N として, N = 1111, 2222, . . . , 9999 以外を入力としなさい N が 3 桁の場合は千の位に 0 を補って 4 桁の整数にすること. 例えば N = 381 ならば, g (381) = g (0381) = 8310 − 0138 = 8172 である. (余力があれば) 5 桁, 6 桁など桁を増やして同様の操作を適用 するとどうなるか確認せよ. 7 / 16 1/7 1/21 課題 5 : (中間値の定理) 中間値の定理 連続関数 f : R → R と区間 [a, b] に対して, もし f (a)f (b) < 0 なら ば f (c) = 0 となる c が開区間 (a, b) に存在する. 中間値の定理の証明の概要 ∞ 次の点列 {an }∞ n=1 と {bn }n=1 を構成した: a = a1 ≤ a2 ≤ · · · ≤ an ≤ · · · ≤ c ≤ · · · ≤ bn ≤ · · · ≤ b2 ≤ b1 = b この時 { an = { bn = ( an−1 +bn−1 2 if f an−1 o.w. an−1 +bn−1 2 if f bn−1 o.w. ( an−1 +bn−1 2 an−1 +bn−1 2 ) <0 , (n = 2, 3, . . . , ) ) >0 ただし f (a) < 0, f (b) > 0 を仮定している limn→∞ an = limn→∞ bn = c が成り立つ (n = 2, 3, . . . , ) 8 / 16 1/7 1/21 課題 5 の続き 中間値の定理の証明をヒントに, 以下の方程式の解 のうち区間 I に入っている解を求めるプログラムを書いて提出し なさい. (1) f (x) = x 3 − 3x + 3 = 0, I = [−3, 0] (2) f (x) = tanh(x) + 0.2x + 0.3 = 0, I = [−1, 1] なお, ファイル名は bisection.java とすること 無限回の反復は不可能なので, 十分小さい ϵ > 0 に対して bn − an < ϵ となれば反復を終了して解 3 を出力させなさい ϵ は 10−6 から 10−8 くらいで良い tanh(x) は Math.tanh(x) で計算できる 3 出力すべき解は an あるいは bn でよい 9 / 16 1/7 1/21 課題 6 : (1 次元ランダムウォーク) x(t) を点 Q の時刻 t での数 直線上の位置とし, x(0) = 0 と定める. 確率 p で表, 確率 1 − p で 裏が出るコインが一枚あるとする. x(t) に対して x(t + 1) を次の 様に定める: { x(t) + 1 (コインが表の時) x(t + 1) = x(t) − 1 (コインが裏の時) この操作を (無限回) 繰り返すと点 Q はいつか原点 0 に戻ってく るだろうか. 10 / 16 1/7 1/21 Figure: p = 0.5 & 100 回の操作 11 / 16 1/7 1/21 Figure: p = 0.5 & 10000 回の操作 12 / 16 1/7 1/21 Figure: p = 0.8 & 100 回の操作 13 / 16 1/7 1/21 Figure: p = 0.8 & 10000 回の操作 14 / 16 1/7 1/21 課題 6 の続き : これを調べるために, 次のプログラムを作成して 提出しなさい. 名前は randwalk.java とすること: 確率 p を入力して, 1000 回ランダムウォークを発生させて, P(p) = 原点に帰ってきた回数 1000 で, 点 Q が原点に返ってくる確率を P(p) で近似して, これを 出力する 1 回のランダムウォークで「無限回の操作」を実施すること は不可能なので, 「100000 回の操作」にする. p を変えて P(p) の値を調べなさい. 特に p がどの値だと必ず 帰ってくるか予想しなさい 4 . 4 余力があれば調べたり, 自分で考えたりして p を特定して証明しなさい 15 / 16 1/7 1/21 課題の提出先 URL : http://bb9.iii.kyushu-u.ac.jp 上記から, 「計算機数学概論」を選択 「コンテンツ」→「1/21・1/28 の課題」をクリック 期限は, 1/28 PM 11:59 です 16 / 16