プログラミング基礎I(再) 山元進
授業評価アンケート 初めにアンケートに答える時間をとる 自由記入欄にも積極的に書きこまれたし
先週の課題の解説 出力を各ケタごとに縦に見ると、7周期に同じ数字が繰り返すケタと、3周期に同じ数字を繰り返すケタとに分かれる グループA: 3周期のケタが3つ グループB: 7周期のケタが7つ 基本的なルールは グループAの中で巡回置換 グループBの中で巡回置換 の2つで、これを実装すれば"良い解答"になる
良い解答を実装するのは、若干難しい そもそも、画面の出力だけみても巡回置換とは分からないので、動作をチェックするプログラムを作る必要がある 画面出力だけから判断すると、特定の数字の下に表示される数字は決まっている それを実装したのが"普通の解答"
id=999; の場合の変換規則 0123456789 1行目は、繰り返し処理の前に実行した print で印字されたもの 8901564237 繰り返し処理 1 回目 3789645012 繰り返し処理 2 回目 1237456890 繰り返し処理 3 回目 9012564378 7890645123 2378456901 0123564789 8901645237 3789456012 1237564890 規則の明文化の例 0→8 9012645378 1→9 7890456123 2→0 2378564901 3→1 0123645789 4→5 8901456237 5→6 3789564012 6→4 1237645890 7→2 9012456378 8→3 7890564123 9→7 2378645901 0123456789 繰り返し処理 21 回目 ここで元の並びに戻ったので繰り返し終了 Finished. 終わったしるしに "Finished." と印字
普通の解答例 (id=999) class Ex01 { public static void permutation(int in[]){ int[] permList = {8,9,0,1,5,6,4,2,3,7}; for(int i=0; i<10; i++){ in[i]=permList[in[i]]; // ここがポイント } public static boolean samePerm(int[] in1, int[] in2){ if(in1.length != in2.length ) return false; boolean flag=true; for(int i=0; i<in1.length; i++) if(in1[i]!=in2[i]) flag=false; return flag; // 一つでも違う数値があったら false を返す public static void main(String[] args){ int[] input1={0,1,2,3,4,5,6,7,8,9}; int[] input2={0,1,2,3,4,5,6,7,8,9}; int id=999; do{ Perm.permutation(id,input1); permutation(input2); if(!samePerm(input1,input2)){ System.out.println("Differ!"); Perm.print(input1); Perm.print(input2); }while(!Perm.isIdentity(input1));
今日は 0~9 の数字を並べて作る順列の生成法を題材にする 良い解答の解答例は示さない permutation メソッドと Perm.permutation メソッドが、どの入力に対しても 一致することを示すためには、入力の生成方法を考える必要あり 入力は、長さ10の整数配列であればなんで良いわけではなく、0~9の数字を並べて作る順列である 今日は 0~9 の数字を並べて作る順列の生成法を題材にする
今日も課題を解くのがメイン 指示をよく読むこと 後の課題で先週利用した Perm.class を使うので、今日の課題プログラムを作成するディレクトリにコピーしておく 学籍番号の下3ケタを 999 と仮定しているサンプルがあるので、それは自分の学籍番号の下3ケタに書きかえる
課題1 (下準備) 0~3 の数字を並べて作る順列(先頭の数字が0である場合を含める)を、小さい順に全てあげよ (解答の最初の3項まで) 0123, 0132, 0213, … 次ページのEx02.java をどう書き換えれば上の並びを出力するようになるか、説明せよ Ex02は、001, 002, 003, という並びを生成して、同じ数字が繰り返し出てきたら排除している この方法は、簡単だが桁数を変えるのが面倒 上のケタから順に数字を決めてゆく際、使った数字のリストを作り、重複を排除すると、もうすこしまし 発想が難しいが、プログラムが短くなる方法もある
0~2 の数字を並べてできる順列を、小さい順に全て表示するプログラム(工夫なし版) class Ex02 { public static void main(String[] args){ int[] input=new int[3]; for(int i0=0; i0<3; i0++){ input[0]=i0; for(int i1=0; i1<3; i1++){ if(i1==i0) continue; input[1]=i1; for(int i2=0; i2<3; i2++){ if(i2==i0 || i2==i1) continue; input[2]=i2; Perm.print(input); }
課題2(出力チェック) 1) 0~5 の数字を並べ替えてできる順列を全てあげるプログラムを作成せよ。ただし順列は、長さ6の整数配列として表されるものとする。 2) この順列は 6! = 720 通りある。1行に1つ順列を表示するとすれば、720行になる。人手で出力をチェックするには行数が多いので、 linux のコマンドを利用してチェックする方法を考えよ。 sort, uniq (, diff, wc) を使えばできる 3) 実際に出力をチェックせよ
課題3(問題の規模拡大) 0~9 の数字を並べ替えてできる順列を全てあげるプログラムを作成せよ。ただし、順列は長さ10の整数配列として表されるものとする。 この課題の出力は 328800 行。人手でのチェックは無理。
課題4(本題) 1) 課題3では、生成した順列を画面(標準出力)に出力した。この課題では、画面に出力するかわりに、次の2つメソッドの入力として使え。 このパワーポイントの6ページにあげた permutation メソッドを、自分の id 用に書き換えたもの 先週の Perm.class の Perm.permutation 注意: メソッドの引数として与えた配列は、上の各メソッドによって書き換えられてしまう。書き換えは、後の操作に支障をきたす。それを避けるため、各メソッドの入力として2つの新たな配列を作り、値をコピーしてから各メソッドの引数とせよ。
課題4(続) 2) 1) 2つのメソッドから戻ってきた後(書き変わった後の)の整数配列を比較し、各要素の値が完全に一致する場合が何通りあるか数えるプログラムをつくれ。
以下はヒント
sort は、ある欄の値をキーにして辞書式に並べ替えるコマンド uniq は、隣り合う行が重複する場合、重複を取り除くコマンド 全ての順列が過不足なく印字されているならば、並べ替えた時に重複する行はないはず 他に有用なコマンド1 : wc 語数を数えるコマンド。 -l オプションをつけると行の数を数える 他に有用なコマンド2 : diff 2つのファイルの違いを画面に表示する
Ex02.java を書き換えて、0~2 の数字を並べて作った順列のうち、210 という順列が何番目の順列かを表示するプログラム class Ex03 { public static boolean equiv(int[] in1, int[] in2){ // このメソッドは引数を書き変えない if(in1.length != in2.length ) return false; boolean flag=true; for(int i=0; i<in1.length; i++) if(in1[i]!=in2[i]) flag=false; return flag; } public static void main(String[] args){ int[] input=new int[3]; int[] ref={2,1,0}; int count=0; for(int i0=0; i0<3; i0++){ input[0]=i0; for(int i1=0; i1<3; i1++){ if(i1==i0) continue; input[1]=i1; for(int i2=0; i2<3; i2++){ if(i2==i0 || i2==i1) continue; input[2]=i2; count++; if(equiv(input,ref)){ System.out.print(count+"番目の順列が"); for(int i=0; i<3; i++) System.out.print(ref[i]); System.out.println("になりました。");
int[] copy1 = input.clone(); 整数配列のコピー int[] copy1 = input.clone(); copy1 が input のコピー(clone)になる copy1 は input とは独立したメモリを確保している copy1 の値を書き換えても input への影響はない int[] alias=input; でも alias が整数配列として扱われるが、データを確保するメモリは input と共有。つまり、alias[i] を書き換えると、 input[i] も同じ値になる。
今回の課題では、 int[] copy1 = input.clone(); int[] copy2 = input.clone(); Perm.permutation(id,copy1); permutation(copy2); if(equiv(copy1,copy2)){ …. } といった使い方を想定している。