[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

コンピュータIIIB(Javaアルゴリズム)第3回

目次
3.1 メソッド
3.1.1 メソッドの基本
3.1.2 値呼出し
3.1.3 2重の呼出し
3.2 再帰(1)
3.2.1 再帰とは
3.2.2 再帰呼出しの仕組み
3.2.3 階乗
3.2.4 フィボナッチ数
3.2.5 ハノイの塔問題
3.3 演習3
3.4 レポート課題
3.5 参考文献
索引
値呼出し   階乗   仮引数   関数   再帰的関数   再帰的手続き   再帰的プログラム   再帰呼出し   実引数   手続き   パスカルの3角形   ハノイの塔問題   引数   フィボナッチ数   メソッド  

3.1 メソッド

3.1.1 メソッドの基本

関数function ) とは、呼び出されるとまとまった計算をして、呼んだ側に計算結果を返すプログラム単位です。 手続きprocedure ) とは、呼び出されるとまとまった処理をして、呼んだ側に戻るプログラム単位です。 Java言語では、関数も手続きも メソッドmethod ) で実現します。

メソッドには、インスタンス・メソッドとクラス・メソッドがあります。 今日は、(同じクラスの)クラス・メソッドのみを扱います。

メソッドは、定義してから呼び出します。 メソッド定義の形式は、関数の場合は

static クラス名 メソッド名 (クラス名 変数名, ...) {
    文の並び
}

となり、手続きの場合は

static void メソッド名 (クラス名 変数名, ...) {
    文の並び
}

となります。 メソッド呼出しの形式は、関数の場合は式として

メソッド名(式, ...)

となり、手続きの場合は文として

メソッド名(式, ...);

となります。

3.1.2 値呼出し

メソッドの括弧の中は、 引数argument ) と呼ばれます。 メソッドと呼ぶ側は、引数を使ってデータをやり取りします。

メソッド呼出しの括弧の中は、 実引数actual argument ) と呼ばれ、メソッド定義の括弧の中は、 仮引数formal argument ) と呼ばれます。 Java言語では、メソッドが呼び出されますと、仮引数の変数が用意され、そこに実引数の式の値が格納されます。 このように呼び出すことを、 値呼出しcall by value ) と言います。

メソッドを呼び出したとき、引数が基本データ型ならば、仮引数に基本型の値がコピーされるだけですので、呼ぶ側のデータに影響はありません。 一方、引数が参照データ型ならば、仮引数に参照が格納されますので、呼ぶ側のデータに影響を与えられます。 この違いを、次の例で理解してください。

/*  1*/ class CallByValueTest {
/*  2*/     public static void main (String[] args) {
/*  3*/         int[] a = new int[1];
/*  4*/         a[0] = 100;
/*  5*/         System.out.println("a[0] = " + a[0]);
/*  6*/         clearVariable(a[0]);
/*  7*/         System.out.println("a[0] = " + a[0]);
/*  8*/         clearArray(a);
/*  9*/         System.out.println("a[0] = " + a[0]);
/* 10*/     }
/* 11*/     static void clearVariable (int n) {
/* 12*/         n = 0;
/* 13*/     }
/* 14*/     static void clearArray (int[] s) {
/* 15*/         s[0] = 0;
/* 16*/     }
/* 17*/ }
b04a001@AsiaA1:~/comp3b% java CallByValueTest
a[0] = 100
a[0] = 100
a[0] = 0
b04a001@AsiaA1:~/comp3b%

3.1.3 2重の呼出し

メソッドの中では、さらに他のメソッドを呼び出すこともできます。 さらに呼び出されたメソッドが終了しますと、呼んだメソッドに制御が戻ります。 この動作を確認するのが、次のプログラムです。

/*  1*/ class DoubleCall {
/*  2*/     public static void main (String[] args) {
/*  3*/         method1();
/*  4*/     }
/*  5*/     static void method1 () {
/*  6*/         System.out.println("The method call method1() begins.");
/*  7*/         method2();
/*  8*/         System.out.println("The method call method1() ends.");
/*  9*/     }
/* 10*/     static void method2 () {
/* 11*/         System.out.println("The method call method2() begins.");
/* 12*/         System.out.println("The method call method2() ends.");
/* 13*/     }
/* 14*/ }
b04a001@AsiaA1:~/comp3b% java DoubleCall
The method call method1() begins.
The method call method2() begins.
The method call method2() ends.
The method call method1() ends.
b04a001@AsiaA1:~/comp3b%

3.2 再帰(1)

3.2.1 再帰とは

再帰とは、自分自身を呼び出すことです。 もちろん、ただ呼び出したのでは、無限ループに陥ってしまいます。 if文などを使い、ある条件のもとで自分を呼び出すのです。 再帰は、問題の種類によっては、分かりやすい問題解決の方法になります。

再帰を用いたプログラムを 再帰的プログラムrecursive program ) と呼びます。 再帰的プログラムで、自分自身を呼び出すことを 再帰呼出しrecursive call ) といいます。 再帰呼出しが行われる手続きと関数は、それぞれ 再帰的手続きrecursive procedure )、 再帰的関数recursive function ) と呼ばれます。

3.2.2 再帰呼出しの仕組み

次のプログラムでは、メソッド method3 の中で、自分自身を呼び出しています。 このプログラムを実行しますと、まず method3 (2) が呼び出され、その途中で method3 (1) が呼び出され、さらにその途中で method3 (0) が呼び出されます。 メソッドの実行が終了しますと、順次、呼んだ側に制御が戻ります。

/*  1*/ class RecursionTest {
/*  2*/     public static void main (String[] args) {
/*  3*/         method3(2);
/*  4*/     }
/*  5*/     static void method3 (int n) {
/*  6*/         System.out.println("The method call method3(" + n + ") begins.");
/*  7*/         if (n > 0) {
/*  8*/             method3(n - 1);
/*  9*/         }
/* 10*/         System.out.println("The method call method3(" + n + ") ends.");
/* 11*/     }
/* 12*/ }
b04a001@AsiaA1:~/comp3b% java RecursionTest
The method call method3(2) begins.
The method call method3(1) begins.
The method call method3(0) begins.
The method call method3(0) ends.
The method call method3(1) ends.
The method call method3(2) ends.
b04a001@AsiaA1:~/comp3b%

3.2.3 階乗

再帰の例として、はじめに、 n の階乗を計算します。 ここで、 n階乗factorial ) とは、1から n までの積のことです。

これを、再帰的に

fact ( n ) = n * fact ( n - 1), fact (0) = 1

と定義します。 すると、例えば fact (3) は、

fact(3) = 3 * fact(2)
        = 3 * 2 * fact(1)
        = 3 * 2 * 1 * fact(0)
        = 3 * 2 * 1 * 1

と計算できます。

/*  1*/ class Factorial {
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println("fact(3) = " + fact(3));
/*  4*/         System.out.println("fact(4) = " + fact(4));
/*  5*/         System.out.println("fact(5) = " + fact(5));
/*  6*/     }
/*  7*/     static int fact (int n) {
/*  8*/         if (n == 0) {
/*  9*/             return 1;
/* 10*/         } else {
/* 11*/             return n * fact(n - 1);
/* 12*/         }
/* 13*/     }
/* 14*/ }
b04a001@AsiaA1:~/comp3b% java Factorial
fact(3) = 6
fact(4) = 24
fact(5) = 120
b04a001@AsiaA1:~/comp3b%

3.2.4 フィボナッチ数

次の例として、フィボナッチ数を取り上げます。 フィボナッチ数Fibonacci number ) とは、

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

のように、1, 1 から始め、直前の2つの数を足して次の数にするという数列です。 この規則は、

fib ( n ) = fib ( n - 1) + fib ( n - 2), fib (0) = fib (1) = 1

のように再帰を使って定義できます。 この定義をそのままプログラムにしますと、フィボナッチ数の計算ができるのです。

/*  1*/ class Fibonacci {
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println("fib(3) = " + fib(3));
/*  4*/         System.out.println("fib(4) = " + fib(4));
/*  5*/         System.out.println("fib(5) = " + fib(5));
/*  6*/     }
/*  7*/     static int fib (int n) {
/*  8*/         if (n == 0 || n == 1) {
/*  9*/             return 1;
/* 10*/         } else {
/* 11*/             return fib(n - 1) + fib(n - 2);
/* 12*/         }
/* 13*/     }
/* 14*/ }
b04a001@AsiaA1:~/comp3b% java Fibonacci
fib(3) = 3
fib(4) = 5
fib(5) = 8
b04a001@AsiaA1:~/comp3b%

3.2.5 ハノイの塔問題

最後の例として、ハノイの塔問題を取り上げます。 ハノイの塔問題tower of Hanoi problem ) とは、大きさのすべて異なる n 枚の円盤が台Aに積み上がっているとき、台Bを利用して、すべての円盤を台Cに移動するという問題です。 ただし、円盤は一度に一枚しか移動できず、小さい円盤の上に大きい円盤はのせられません。

例えば、円盤が3枚ですと、初期状態は

ハノイの塔(1)
図 3.1  ハノイの塔(1)

であり、最終状態は

ハノイの塔(8)
図 3.2  ハノイの塔(8)

です。

この問題は、次のように再帰を使って解決できます。

  1. n - 1 枚を、台 x から台 z を利用して台 y へ移動する。
  2. 残った1枚を、台 x から台 z へ移動する。
  3. n - 1 枚を、台 y から台 x を利用して台 z へ移動する。

ただし、 n が正でないときは何もしません。

n が3の場合は次のようになります。

ハノイの塔(1)
図 3.3  ハノイの塔(1)

まず、上の2枚を、台Aから台Cを利用して台Bへ移動します。

ハノイの塔(2)
図 3.4  ハノイの塔(2)
ハノイの塔(3)
図 3.5  ハノイの塔(3)
ハノイの塔(4)
図 3.6  ハノイの塔(4)

次に、残った1枚を、台Aから台Cへ移動します。

ハノイの塔(5)
図 3.7  ハノイの塔(5)

最後に、2枚を、台Bから台Aを利用して台Cへ移動します。

ハノイの塔(6)
図 3.8  ハノイの塔(6)
ハノイの塔(7)
図 3.9  ハノイの塔(7)
ハノイの塔(8)
図 3.10  ハノイの塔(8)

再帰メソッドの名前は hanoi とします。 引数は、台の文字を表す x , y , z と枚数を表す n です。

/*  1*/ class HanoiTower {
/*  2*/     public static void main (String[] args) {
/*  3*/         hanoi('A', 'B', 'C', 4);
/*  4*/     }
/*  5*/     static void hanoi (char x, char y, char z, int n) {
/*  6*/         if (n > 0) {
/*  7*/             hanoi(x, z, y, n - 1);
/*  8*/             System.out.println(x + " to " + z);
/*  9*/             hanoi(y, x, z, n - 1);
/* 10*/         }
/* 11*/     }
/* 12*/ }
b04a001@AsiaA1:~/comp3b% java HanoiTower
A to B
A to C
B to C
A to B
C to A
C to B
A to B
A to C
B to C
B to A
C to A
B to C
A to B
A to C
B to C
b04a001@AsiaA1:~/comp3b%

3.3 演習3

パスカルの3角形を計算するプログラムを作成してください。 ここで、 パスカルの3角形Pascal's triangle ) とは、3角形の辺には1を書き、内部には左肩と右肩の和を書いて得られる図形のことです。

              1
            1   1
          1   2   1
        1   3   3   1
      1   4   6   4   1
    1   5   10  10  5   1
  :                       :

0番目から数え始め、上から n 番目、左から r 番目の数を、 combi ( n , r ) と書くことにします。 すると、パスカルの3角形は次のように定義できます。

combi ( n , r ) = combi ( n - 1, r - 1) + combi ( n - 1, r ),
combi ( n , 0) = combi ( n , n ) = 1

この定義にしたがってメソッドを定義し、このメソッドを呼び出して、 combi (5, 0), combi (5, 1), combi (5, 2) を計算してください。

b04a001@AsiaA1:~/comp3b% java PascalTriangle
combi(5, 0) = 1
combi(5, 1) = 5
combi(5, 2) = 10
b04a001@AsiaA1:~/comp3b%

余力のある人は、パスカルの3角形を8行分計算してください。 出力が左揃えになっても構いません。

b04a001@AsiaA1:~/comp3b% java PascalTriangle2
 1
 1 1
 1 2 1
 1 3 3 1
 1 4 6 4 1
 1 5 10 10 5 1
 1 6 15 20 15 6 1
 1 7 21 35 35 21 7 1
b04a001@AsiaA1:~/comp3b%

3.4 レポート課題

今日の演習3の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b04a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月12日)を明記してください。


3.5 参考文献


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2007年10月12日更新
小西 善二郎 <konishi@cis.twcu.ac.jp>
Copyright (C) 2007 Zenjiro Konishi. All rights reserved.