繰返しの繰返し、すなわち、繰返し文の中にさらに繰返し文を書くこともできます。 このような制御構造を、2重ループと呼ぶことがあります。 3重ループやそれ以上も考えられます。
一般的に、2重ループではループ制御変数を2つ用意します。 変数が i と j ならば、 i を外側のループ制御変数とし、 j を内側のループ制御変数とします。
次のプログラムは、出力を4回繰り返すことを3回繰り返すものです。 変数 i と j がどのように変化するかを見てください。
/* 1*/ class DoubleLoop { /* 2*/ public static void main (String[] args) { /* 3*/ int i, j; /* 4*/ for (i = 0; i < 3; i++) { /* 5*/ for (j = 0; j < 4; j++) { /* 6*/ System.out.println("i = " + i + ", j = " + j + "."); /* 7*/ } /* 8*/ } /* 9*/ } /* 10*/ }
asiaa1:~/comp3b b08a001$ java DoubleLoop i = 0, j = 0. i = 0, j = 1. i = 0, j = 2. i = 0, j = 3. i = 1, j = 0. i = 1, j = 1. i = 1, j = 2. i = 1, j = 3. i = 2, j = 0. i = 2, j = 1. i = 2, j = 2. i = 2, j = 3. asiaa1:~/comp3b b08a001$
2重ループでは、内側の繰返しの回数を変えることもできます。 次のプログラムでは、出力を0回繰り返し、1回繰り返し、…としています。
/* 1*/ class TriangleLoop { /* 2*/ public static void main (String[] args) { /* 3*/ int i, j; /* 4*/ for (i = 0; i < 5; i++) { /* 5*/ for (j = 0; j < i; j++) { /* 6*/ System.out.println("i = " + i + ", j = " + j + "."); /* 7*/ } /* 8*/ } /* 9*/ } /* 10*/ }
asiaa1:~/comp3b b08a001$ java TriangleLoop i = 1, j = 0. i = 2, j = 0. i = 2, j = 1. i = 3, j = 0. i = 3, j = 1. i = 3, j = 2. i = 4, j = 0. i = 4, j = 1. i = 4, j = 2. i = 4, j = 3. asiaa1:~/comp3b b08a001$
2重ループの例として、第1回の授業のアンケートの問題を考えます。 ここで、問題をもう一度書きます。
100以下の素数(25個)をすべて求めるプログラムが書けるかどうか答えてください。 ここで、素数とは1と自分以外に約数を持たない数のことです。 2, 3, 5などが素数です。 プログラミングの方針はいくつか考えられます。 例えば、2 * 2, 2 * 3, 3 * 3, ... のように掛け算を次々と実行し、それらの計算結果は素数でないという方針だと、次のようになります。 まず、添字が2から100までの配列要素に1を格納します。
a[2] = 1; a[3] = 1; ...; a[100] = 1;
次に、添字が2の倍数である配列要素に0を格納します。
a[2 * 2] = 0; a[2 * 3] = 0; ...; a[2 * 50] = 0;
続いて、添字が3の倍数である配列要素に0を格納します。
a[3 * 2] = 0; a[3 * 3] = 0; ...; a[3 * 33] = 0;
さらに、添字が4の倍数である配列要素に0を格納します。
a[4 * 2] = 0; a[4 * 3] = 0; ...; a[4 * 25] = 0;
この作業を、添字が50の倍数の場合まで行います。 最後に、値が1である配列要素の添字をすべて出力します。
このプログラムは次のようになります。
/* 1*/ class PrimeNumber { /* 2*/ public static void main (String[] args) { /* 3*/ int i, j; /* 4*/ int[] a = new int[101]; /* 5*/ for (i = 2; i <= 100; i++) { /* 6*/ a[i] = 1; /* 7*/ } /* 8*/ for (i = 2; i <= 100 / 2; i++) { /* 9*/ for (j = 2; j <= 100 / i; j++) { /* 10*/ a[i * j] = 0; /* 11*/ } /* 12*/ } /* 13*/ for (i = 2; i <= 100; i++) { /* 14*/ if (a[i] == 1) { /* 15*/ System.out.println(i); /* 16*/ } /* 17*/ } /* 18*/ } /* 19*/ }
asiaa1:~/comp3b b08a001$ java PrimeNumber 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 asiaa1:~/comp3b b08a001$
よく考えると、4の倍数を素数でないと決めるのは無駄です。 なぜなら、4の倍数は2の倍数なので、すでに素数でないと決められているからです。 上記のプログラムは、いくつかの改良の余地があります。
素数を求めるこのアルゴリズムは、 エラトステネスのふるい と呼ばれています。
配列は、数学における集合と見なすことができます。 つまり、配列
を、要素が 10, 20, 30, 40, 50 の集合と考えるのです。 偶然にも、配列の初期化で使われる記号 {...} と、集合を表す記号 {...} は同じなので、理解しやすいと思います。 配列と集合の違いは、配列は要素の重複が許されるのに対して、集合は要素の重複が許されないことです。
集合の基本演算には、
などがあげられます。
集合AとBの和集合は、A∪Bと表され、AかBの少なくとも一方に属する要素の集合です。 例えば、
となります。
集合AとBの共通部分は、A∩Bと表され、AとBの両方に属する要素の集合です。 例えば、
となります。
次のプログラムは、集合 A = {10, 20, 30, 40, 50} と B = {10, 30, 50, 70, 90} の和集合を出力するプログラムです。 A の要素をすべて出力してから、A に属さない B の要素を出力しています。
/* 1*/ class ArrayUnion { /* 2*/ public static void main (String[] args) { /* 3*/ int i, j; /* 4*/ boolean found; /* 5*/ int[] a = {10, 20, 30, 40, 50}; /* 6*/ int[] b = {10, 30, 50, 70, 90}; /* 7*/ for (i = 0; i < a.length; i++) { /* 8*/ System.out.println(a[i]); /* 9*/ } /* 10*/ for (i = 0; i < b.length; i++) { /* 11*/ found = false; /* 12*/ for (j = 0; j < a.length; j++) { /* 13*/ if (b[i] == a[j]) { /* 14*/ found = true; /* 15*/ break; /* 16*/ } /* 17*/ } /* 18*/ if (!found) { /* 19*/ System.out.println(b[i]); /* 20*/ } /* 21*/ } /* 22*/ } /* 23*/ }
asiaa1:~/comp3b b08a001$ java ArrayUnion 10 20 30 40 50 70 90 asiaa1:~/comp3b b08a001$
集合 A = {10, 20, 30, 40, 50} と B = {10, 30, 50, 70, 90} の共通部分を出力するプログラムを作成してください。 A に属する B の要素を出力する、と考えるとよいでしょう。
asiaa1:~/comp3b b08a001$ java ArrayIntersection 10 30 50 asiaa1:~/comp3b b08a001$
今日の演習3の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b08a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月10日)を明記してください。