課題0901
コンソールから正の整数nを入力させ、1からnまでの整数の和を表示するプログラム AddByRecursion.java をパッケージ j1.lesson09 に作成しなさい。
和の計算を行うメソッドの名前は addUp とし、1つの引数 (int n) をもち、1からnまでの和を返す。また、このメソッドは必ず再帰呼び出しによって実装すること。
再帰的定義によって和addUp(n) を定義すると、以下のようになる。
addUp(1) = 1 addUp(n) = n + addUp(n-1) (n>1)
このプログラムではまずmainメソッド内でコンソールの入力から1つの整数を取得し、それを用いてaddUpメソッド内で和を計算し、最後にmainメソッド内で結果を表示する。
コンソールへの入力要求は次のように表示する。
正の整数を入力:
結果の表示方法は次の通りとする。
nまでの和はx
ただし、nは入力された正の整数、xは和とする。
結果の例
仮に、nとして10を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
正の整数を入力: 10 10までの和は55
手順
指定した箇所で必ずテストを行うこと。
- パッケージ j1.lesson09 にクラス AddByRecursion を作成 (ファイル名は AddByRecursion.javaとなる)
- クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- クラス内に必要なメソッド (addUp) の骨格を作成 (こちらには throws IOException は必要ない)
- テスト項目「AddByRecursion に対する骨格テスト」をパス
- main以外のメソッド内に課題の内容を実装
- mainから各メソッドを呼び出して、メソッドが正しく動作しているか確認
- テスト項目「AddByRecursion に対する単体テスト」をパス
- mainメソッド内に課題の内容を実装
- いくつかテストを行って、プログラムが正しく動作しているか確認
- テスト項目「AddByRecursion に対する機能テスト」をパス
課題0902
コンソールから正の整数を2つ入力させ、それらの最大公約数(Greatest Common Divisor, GCDと略す。)を表示するプログラム Gcd.java をパッケージ j1.lesson09 に作成しなさい。
「引数で与えられた2つの整数の最大公約数を返すメソッド」を、名前が gcd で2つの整数を引数として取る (int, int) ようなメソッドとして作成すること。このメソッドでは与えられた二つの引数の最大公約数を int 型の整数として返す。今回、このメソッドが再帰的に呼び出されることになる。2つの引数のいずれかが負の値であった場合は考慮しなくて良い。
与えられた2つの整数 m, n の最大公約数を求めるアルゴリズム gcd(m,n) は次のように表せる。
gcd(m,n) = m (n = 0) gcd(m,n) = gcd(n, (mをnで割った余り)) (n != 0)
このプログラムではまずmainメソッド内でコンソールの入力から2つの整数を取得し、それを用いてgcdメソッド内でGCDを計算し、最後にmainメソッド内で結果を表示する。
コンソールへの第1の入力要求は次のように表示する。
整数mを入力:
コンソールへの第2の入力要求は次のように表示する。
整数nを入力:
このとき、入力データの不正はチェックしなくもてよい。
結果の表示方法は次の通りとする。
mとnの最大公約数はx
ただし、m,nは入力された正の整数、xはGCDとする。
結果の例
仮に、mとして16、nとして12を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
正の整数mを入力: 16 正の整数nを入力: 12 16と12の最大公約数は4
手順
指定した箇所で必ずテストを行うこと。
- パッケージ j1.lesson09 にクラス Gcd を作成 (ファイル名は Gcd.javaとなる)
- クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- クラス内に必要なメソッド (gcd) の骨格を作成 (こちらには throws IOException は必要ない)
- テスト項目「Gcd に対する骨格テスト」をパス
- main以外のメソッド内に課題の内容を実装
- mainから各メソッドを呼び出して、メソッドが正しく動作しているか確認
- テスト項目「Gcd に対する単体テスト」をパス
- mainメソッド内に課題の内容を実装
- いくつかテストを行って、プログラムが正しく動作しているか確認
- テスト項目「Gcd に対する機能テスト」をパス
課題0903 (optional)
コンソールから全体の要素数と選び出す要素数を入力させ、その組み合わせの総数を表示するプログラム PascalTriangle.java をパッケージ j1.lesson09 に作成しなさい。
組み合わせ総数の計算を行うメソッドの名前は combination とし、2つの引数 (int n, int r) をもつ。引数で与えられた n 個の要素から r 個の要素を選び出す組み合わせの総数を返す (引数の名前は変えてもかまわない)。また、このメソッドは必ず再帰呼び出しによって実装すること。
再帰的定義によって組み合わせの総数 combination(n,r) を定義すると、以下のようになる。
combination(n,r) = 1 (r = 0) combination(n,r) = 1 (r = n) combination(n,r) = combination(n-1, r-1) + combination(n-1, r) (r != 0 かつ r != n)
このプログラムではまずmainメソッド内でコンソールの入力から2つの整数を取得し、それらを用いてcombinationメソッド内で組み合わせの総数を計算し、最後にmainメソッド内で結果を表示する。
コンソールへの入力要求は次のように表示する。
- 全体の要素数
- この直後に全体の要素数を入力させる
全体の要素数を入力:
- 選び出す要素数
- この直後に選び出す要素数を入力させる
選び出す要素数を入力:
結果の表示方法は次の通りとする。
nからrを選び出す組み合わせはx通り
ただし、nは全体の要素数、rは選び出す要素数、xは組み合わせの総数とする。
結果の例
仮に、全体の要素数に5、選び出す要素数に3を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
全体の要素数を入力:5 選び出す要素数を入力:3 5から3を選び出す組み合わせは10通り
手順
指定した箇所で必ずテストを行うこと。
- パッケージ j1.lesson09 にクラス PascalTriangle を作成 (ファイル名は PascalTriangle.javaとなる)
- クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- クラス内に必要なメソッド (combination) の骨格を作成 (こちらには throws IOException は必要ない)
- テスト項目「PascalTriangle に対する骨格テスト」をパス
- main以外のメソッド内に課題の内容を実装
- mainから各メソッドを呼び出して、メソッドが正しく動作しているか確認
- テスト項目「PascalTriangle に対する単体テスト」をパス
- mainメソッド内に課題の内容を実装
- いくつかテストを行って、プログラムが正しく動作しているか確認
- テスト項目「PascalTriangle に対する機能テスト」をパス
ヒント (?)
課題のクラス名である PascalTriangle とは、以下のような三角形のことである。
この三角形は、各行の左端と右端が 1 で、それ以外は左上にある数と右上にある数の和である。
この三角形には面白い性質があって、それぞれの値は以下のように組み合わせ総数計算でよく使用される nCr の形で表すことができる。
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
(メソッド名), throws ... | メソッドを作る際に throws... の指定がない |
単体テスト
メッセージ | 詳細 |
---|---|
(メソッド名), 期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
単体テストの項目
項目名 | 詳細 |
---|---|
testCombination_1_0 | combination メソッドを 実引数 (1, 0) で起動した |
testCombination_1_1 | combination メソッドを 実引数 (1, 1) で起動した |
testCombination_2_0 | combination メソッドを 実引数 (2, 0) で起動した |
testCombination_2_1 | combination メソッドを 実引数 (2, 1) で起動した |
testCombination_2_2 | combination メソッドを 実引数 (2, 2) で起動した |
testCombination_5_0 | combination メソッドを 実引数 (5, 0) で起動した |
testCombination_5_1 | combination メソッドを 実引数 (5, 1) で起動した |
testCombination_5_2 | combination メソッドを 実引数 (5, 2) で起動した |
testCombination_5_3 | combination メソッドを 実引数 (5, 3) で起動した |
testCombination_5_4 | combination メソッドを 実引数 (5, 4) で起動した |
testCombination_5_5 | combination メソッドを 実引数 (5, 5) で起動した |
機能テスト
メッセージ | 詳細 |
---|---|
余計な入力を待っていると考えられます | コンソール入力を取得する命令を4回以上行っていないか確認 |
次の入力を変換できませんでした (???) | ??? を取得しようとして失敗している。コンソール入力を取得する命令を確認 |
期待された結果と異なります | 出力された結果が期待された値と異なる。「期待された値」と「実際の値」を比較 (エラーメッセージが出力されているエリアの下のほうにあるバーをスクロールさせれば見ることができる) し確認。他にも直接プログラムを実行して結果を調べたり、エラーメッセージの2行目にあるat以下を参考にプログラムを見直す |
機能テストの項目
項目名 | 詳細 |
---|---|
test3_2 | 入力が順に 3, 2 |
test4_2 | 入力が順に 4, 2 |
test4_3 | 入力が順に 4, 3 |
test5_2 | 入力が順に 5, 2 |
test7_3 | 入力が順に 7, 3 |
test10_5 | 入力が順に 10, 5 |
課題0904 (optional)
コンソールから整数で分子と分母を入力させ、その既約分数を表示するプログラム LowestTerm.java をパッケージ j1.lesson09 に作成しなさい。
ただし、「引数で与えられた2つの整数の最大公約数を返すメソッド」を、gcdという名前で作成すること。このメソッドでは与えられた二つの引数の最大公約数を int 型の整数として返す。今回、このメソッドが再帰的に呼び出されることになる。2つの引数のいずれかが負の値であった場合は考慮しなくて良い。
課題0902のものと同じでよい。
与えられた2つの整数 m, n の最大公約数を求めるアルゴリズム gcd(m,n) は次のように表せる。
gcd(m,n) = m (n = 0) gcd(m,n) = gcd(n, (mをnで割った余り)) (n != 0)
さらに、「引数で与えられた分子、分母を既約分数として表示するメソッド」を、名前が printWithReduce で2つの整数を引数として取る (int,int) ようなメソッドとして作成すること。一つ目の引数を分子、二つ目の引数を分母とし、それぞれ1以上の整数が与えられるものとして、それ以外の場合は考慮しない。与えられた分子、分母を既約分数になるまで約分して以下のように表示させる。
n / d
ただし、上記は既約分数とし、nは分子でdは分母とする。約分によって整数 n で表せる場合は n / 1 と表示すること。このメソッドの戻り値型は void とする。
このプログラムではまずmainメソッド内でコンソールの入力から2つの整数を取得し、それらを分子、分母としてprintWithReduceに渡す。printWithReduceメソッド内では与えられた分子分母を既約分数になるまで約分し (gcdを使用する)、約分した結果を表示する。
コンソールへの入力要求は次のように表示する。
- 分子
- この直後に分子を入力させる
分子を入力:
- ただし、1以上の値以外が入力されたら、以下のように表示して終了する。
1以上の整数を入力してください
- 分母
- この直後に分母を入力させる
分母を入力:
- ただし、1以上の値以外が入力されたら、以下のように表示して終了する。
1以上の整数を入力してください
結果の表示方法は次の通りとする。
n / d
ただし、n は既約分数に変換された後の分子、d は既約分数に変換された後の分母とする。
結果の例
分子に8、分母に12を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
分子を入力:8 分母を入力:12 2 / 3
分子に8、分母に-1を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
分子を入力:8 分母を入力:-1 1以上の整数を入力してください
分子に-1を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
分子を入力:-1 1以上の整数を入力してください
手順
指定した箇所で必ずテストを行うこと。
- パッケージ j1.lesson09 にクラス LowestTerm を作成 (ファイル名は LowestTerm.javaとなる)
- クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- クラス内に必要なメソッド (printWithReduce) の骨格を作成 (こちらには throws IOException は必要ない)
- クラス内に必要なメソッド (gcd) の骨格を作成 (こちらには throws IOException は必要ない)
- テスト項目「LowestTerm に対する骨格テスト」をパス
- main以外のメソッド内に課題の内容を実装
- mainから各メソッドを呼び出して、メソッドが正しく動作しているか確認
- テスト項目「LowestTerm に対する単体テスト」をパス
- mainメソッド内に課題の内容を実装
- いくつかテストを行って、プログラムが正しく動作しているか確認
- テスト項目「LowestTerm に対する機能テスト」をパス
ヒント
この回では、以下のようにプログラムを分割すればよい。
- main メソッド内
- 入力を受け取り、printWithReduce メソッドを呼び出す。
- printWithReduce メソッド内
- 分母、分子をそれらの最大公約数でそれぞれ割ることで既約分数へ変換し、それを表示する。
- gcd メソッド内
- 再帰呼び出しによって与えられた2つの整数の最大公約数を計算し、それを返す。
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
(メソッド名), throws ... | メソッドを作る際に throws... の指定がない |
単体テスト
メッセージ | 詳細 |
---|---|
(メソッド名), 期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
単体テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
testGcd_1_1 | gcd メソッドを 実引数 (1,1) で起動した |
testGcd_100_100 | gcd メソッドを 実引数 (100,100) で起動した |
testGcd_8_12 | gcd メソッドを 実引数 (8,12) で起動した |
testGcd_360_256 | gcd メソッドを 実引数 (360,256) で起動した |
testPrintWithReduce_1_1 | printWithReduce メソッドを 実引数 (1,1) で起動した |
testPrintWithReduce_100_100 | printWithReduce メソッドを 実引数 (100,100) で起動した |
testPrintWithReduce_8_12 | printWithReduce メソッドを 実引数 (8,12) で起動した |
testPrintWithReduce_360_256 | printWithReduce メソッドを 実引数 (360,256) で起動した |
機能テスト
メッセージ | 詳細 |
---|---|
余計な入力を待っていると考えられます | コンソール入力を取得する命令を2回以上行っていないか確認 |
次の入力を変換できませんでした (???) | ??? を取得しようとして失敗している。コンソール入力を取得する命令を確認 |
期待された結果と異なります | 出力された結果が期待された値と異なる。「期待された値」と「実際の値」を比較 (エラーメッセージが出力されているエリアの下のほうにあるバーをスクロールさせれば見ることができる) し確認。他にも直接プログラムを実行して結果を調べたり、エラーメッセージの2行目にあるat以下を参考にプログラムを見直す |
無限ループの可能性 | 一定時間内にプログラムが終了しなかった。ループが無限に続いていないか確認 |
機能テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
testM5 | 入力が -5 |
test0 | 入力が 0 |
test1_M5 | 入力が順に 1, -5 |
test1_0 | 入力が順に 1, 0 |
test1_1 | 入力が順に 1, 1 |
test15_25 | 入力が順に 15, 25 |
test144_360 | 入力が順に 144, 360 |
test257_259 | 入力が順に 257, 259 |
課題0905 (optional)
コンソールから0以上の整数 k を入力させ、フィボナッチ数列の第 k 項と、再帰メソッドの起動回数を表示するプログラム FibonacciOpt.java をパッケージ j1.lesson09 に作成しなさい。
ただし、フィボナッチ数列の計算は通常の再帰メソッドで実装すると非常に計算量が多くなるので、ある程度簡単な計算方法で算出できるようにする。
(具体的には演習の環境で、第46項を5秒以内に計算できること。)
また、「与えられた引数 k に対して、フィボナッチ数列の第 k 項を返すメソッド」を、名前が fibonacci で1つの整数を引数として取る(int)ようなメソッドとして作成すること。戻り値の型は int とする。
このプログラムではまずmainメソッド内でコンソールの入力から1つの整数を取得し、それを fibonacci(int) に渡す。その計算結果をmainメソッド内で表示し、さらにメソッドの起動回数もmainメソッド内で表示する。メソッドの起動回数は適切な場所でカウントすること。
0以上の整数を入力させる、コンソールへの入力要求は次のように表示する。
0以上の整数を入力:
0以上の値以外が入力されたら、以下のように表示して終了する。
0以上の整数を入力してください
結果の表示方法は次の通りとする。
fibonacci( k ) = x; 起動回数は c 回
ただし、k は入力された整数、x はフィボナッチ数列の第 k 項、c は再帰メソッドの起動回数とする (cはいくらかずれても構わない)。
結果の例
入力に46を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている (必ずしも46回でなくても良い)。
0以上の整数を入力:46 fibonacci(46) = 1836311903 起動回数は46回
入力に-1を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
0以上の整数を入力:-1 0以上の整数を入力してください
手順
指定した箇所で必ずテストを行うこと。
- パッケージ j1.lesson09 にクラス FibonacciOpt を作成 (ファイル名は FibonacciOpt.javaとなる)
- クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- クラス内に必要なメソッド (fibonacci) の骨格を作成 (こちらには throws IOException は必要ない)
- テスト項目「FibonacciOpt に対する骨格テスト」をパス
- main以外のメソッド内に課題の内容を実装
- mainから各メソッドを呼び出して、メソッドが正しく動作しているか確認
- テスト項目「FibonacciOpt に対する単体テスト」をパス
- mainメソッド内に課題の内容を実装
- いくつかテストを行って、プログラムが正しく動作しているか確認
- テスト項目「FibonacciOpt に対する機能テスト」をパス
ヒント
この回では、以下のようにプログラムを分割すればよい。
- main メソッド内
- 入力を受け取り、fibonacci メソッドから結果を受け取り、起動回数と共に表示する。
- fibonacci メソッド内
- 与えられた引数に対して、fastFib メソッドを利用してフィボナッチ数列の第 k 項を返す。
- フィボナッチ数列の定義は、第09回目演習?を参照のこと。
フィボナッチ数列を高速に計算するために、補助メソッド fastFib(int n,int fk,int fk1) メソッドを作成すると良い (このメソッドは課題の仕様に含まれていない)。
- 引数に与えられた fk0 を fib(k), fk1 を fib(k+1) として、fib(k+n)を返す。
- このメソッドは、与えられたフィボナッチ数列の n 項先の値を計算する。
- 以下のように再帰的に定義できる。
fastFib(0, fib(k), fib(k+1)) = fib(k) fastFib(1, fib(k), fib(k+1)) = fib(k + 1) fastFib(n, fib(k), fib(k+1)) = fastFib(n-1, fib(k+1), fib(k+2)) (n > 1) = fastFib(n-1, fib(k+1), fib(k+1) + fib(k)) (n > 1)
- ここで、k = 0として、fib(k) = 0, fib(k+1) = 1 を代入すると、以下のように与えられた n に対して高速に fib(n) が計算できる 。
fastFib(0, 0, 1) = 0 = fib(0) fastFib(1, 0, 1) = 1 = fib(1) fastFib(2, 0, 1) = fastFib(1, 1, 1) = 1 = fib(2) fastFib(3, 0, 1) = fastFib(2, 1, 1) = fastFib(1, 1, 1 + 1) = 2 = fib(3) fastFib(4, 0, 1) = fastFib(3, 1, 1) = fastFib(2, 1, 1 + 1) = fastFib(1, 2, 1 + 2) = 3 = fib(4) ... fastFib(n, 0, 1) = ... = fib(n)
- ただし、n は0以上の整数である。それ以外の数が指定された場合は考慮しなくて良い。
- また、起動回数を計算するため、クラスフィールドとして回数を保存する変数を用意し、起動されるたびカウントする。
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
(メソッド名), throws ... | メソッドを作る際に throws... の指定がない |
単体テスト
メッセージ | 詳細 |
---|---|
(メソッド名), 期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
単体テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
testFibonacci_0 | fibonacci(int) を 実引数 (0) で起動 |
testFibonacci_1 | fibonacci(int) を 実引数 (1) で起動 |
testFibonacci_2 | fibonacci(int) を 実引数 (2) で起動 |
testFibonacci_3 | fibonacci(int) を 実引数 (3) で起動 |
testFibonacci_10 | fibonacci(int) を 実引数 (10) で起動 |
testFibonacci_15 | fibonacci(int) を 実引数 (15) で起動 |
testFibonacci_20 | fibonacci(int) を 実引数 (20) で起動 |
testFibonacci_30 | fibonacci(int) を 実引数 (30) で起動 |
testFibonacci_40 | fibonacci(int) を 実引数 (40) で起動 |
testFibonacci_46 | fibonacci(int) を 実引数 (46) で起動 |
機能テスト
メッセージ | 詳細 |
---|---|
余計な入力を待っていると考えられます | コンソール入力を取得する命令を2回以上行っていないか確認 |
次の入力を変換できませんでした (???) | ??? を取得しようとして失敗している。コンソール入力を取得する命令を確認 |
期待された結果と異なります | 出力された結果が期待された値と異なる。「期待された値」と「実際の値」を比較 (エラーメッセージが出力されているエリアの下のほうにあるバーをスクロールさせれば見ることができる) し確認。他にも直接プログラムを実行して結果を調べたり、エラーメッセージの2行目にあるat以下を参考にプログラムを見直す |
無限ループの可能性 | 一定時間内にプログラムが終了しなかった。ループが無限に続いていないか確認 |
機能テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
testM5 | 入力が -5 |
testM1 | 入力が -1 |
test0 | 入力が 0 |
test1 | 入力が1 |
test2 | 入力が 2 |
test10 | 入力が 10 |
test15 | 入力が 15 |
test20 | 入力が 20 |
test30 | 入力が 30 |
test46 | 入力が 46 |
課題0906 (optional)
コンソールから1以上の整数を入力させ、入力された値だけ円盤を持つ「ハノイの塔」の問題を解く流れを表示するプログラム Hanoi.java をパッケージ j1.lesson09 に作成しなさい。
「ハノイの塔」というのは、次のような問題である。
3つの柱がある。そのうちの1つの柱に穴のあいた円盤を何枚か通して積み重ねてある。
それらの円盤は下のものほど大きく、上のものほど小さい。
そのすべての円盤を以下の規則に従って動かし、指定した柱に円盤を全て移動させる。
- 円盤は、一度に一つだけ移動させることができる
- 円盤は、それより小さな円盤の上に積むことはできない
「ハノイの塔」の問題を実際に体験できるので、以下のサイトを参照するとよい。
http://www.k.hosei.ac.jp/~nakata/JavaProgramming/hanoi/Towers.html
ただし、「引数に指定された値を円盤の数として、その円盤の数を持つハノイの塔を解き手順を以下のように表示するメソッド」を、名前が hanoi で1つの整数を引数として取る (int) ようなメソッドとして作成すること (円盤の数が0以下の場合は考慮しない)。このメソッドの戻り値型は void とする。
このメソッドが呼び出されると、以下のように円盤を動かす様を初めから終わりまで順番に表示する。
円盤の番号: 移動元の塔の番号 -> 移動先の塔の番号
ただし、円盤の番号は小さい順に1,2,3,...と割り振られる。また、塔の番号は次のように割り振られる。
- 最初に円盤が置いてある場所
- 円盤を移動させる際に経由させる場所
- 円盤を移動させる先
このプログラムではまずmainメソッド内でコンソールの入力から1つの整数を取得し、それを hanoi(int) に渡す。hanoi(int) 内で円盤を動かす処理を表示させ、全ての円盤を動かす処理が終わったらプログラムを終了させる。
円盤の数を入力させる、コンソールへの入力要求は次のように表示する。
円盤の数を入力:
1以上の整数以外が入力されたら、以下のように表示して終了する。
1以上の整数を入力してください
結果の表示方法は次の通りとする。
n: from -> to ...
ただし、nは円盤の番号(小さい順に1,2,3,...)、from は移動元の塔の番号、to は移動先の塔の番号とし、円盤がすべて移動しおわるまで表示を繰り返す。
結果の例
円盤の数に3を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。
円盤の数を入力:3 1: 1 -> 3 2: 1 -> 2 1: 3 -> 2 3: 1 -> 3 1: 2 -> 1 2: 2 -> 3 1: 1 -> 3
手順
指定した箇所で必ずテストを行うこと。
- パッケージ j1.lesson09 にクラス Hanoi を作成 (ファイル名は Hanoi.javaとなる)
- クラス Hanoi 内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
- 必ず throws IOException の記述を付加しておくこと (つまり、import java.io.*;も必要となる)
- クラス内に必要なメソッド (hanoi) の骨格を作成 (こちらには throws IOException は必要ない)
- テスト項目「Hanoi に対する骨格テスト」をパス
- main以外のメソッド内に課題の内容を実装
- mainから各メソッドを呼び出して、メソッドが正しく動作しているか確認
- テスト項目「Hanoi に対する単体テスト」をパス
- mainメソッド内に課題の内容を実装
- いくつかテストを行って、プログラムが正しく動作しているか確認
- テスト項目「Hanoi に対する機能テスト」をパス
ヒント
「引数で指定した塔の番号 start から target へ n 個の円盤を移動する手順を表示するメソッド」として、hanoiView などを用意し、再帰的に呼び出すとよい。
ハノイの塔は再帰を用いた考え方をすると、次のように考えることができる。
- (最初に円盤が置いてある場所) から (経由させる場所) へ n - 1 個の円盤を移す (n-1段のハノイの塔)
- (最初に円盤が置いてある場所) には一番大きな円盤が残るので、それを (円盤を移動させる先) へ移す
- (経由させる場所) から (円盤を移動させる先) へ n - 1 個の円盤を移す (n-1段のハノイの塔)
このように考えると、1, 3 はn-1個の円盤を移すハノイの塔であると考えることができ、2 は単純に円盤を一つ移すだけの作業になる。
テストの失敗メッセージ
骨格テスト
メッセージ | 詳細 |
---|---|
(クラス名), existence | パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値型の指定を間違っている。正しくは <T> |
(メソッド名), throws ... | メソッドを作る際に throws... の指定がない |
単体テスト
メッセージ | 詳細 |
---|---|
(メソッド名), 期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照 |
単体テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
testHanoi_1 | hanoi(int) メソッドを 実引数 (1) で起動した |
testHanoi_2 | hanoi(int) メソッドを 実引数 (2) で起動した |
testHanoi_3 | hanoi(int) メソッドを 実引数 (3) で起動した |
testHanoi_4 | hanoi(int) メソッドを 実引数 (4) で起動した |
機能テスト
メッセージ | 詳細 |
---|---|
余計な入力を待っていると考えられます | コンソール入力を取得する命令を2回以上行っていないか確認 |
次の入力を変換できませんでした (???) | ??? を取得しようとして失敗している。コンソール入力を取得する命令を確認 |
期待された結果と異なります | 出力された結果が期待された値と異なる。「期待された値」と「実際の値」を比較 (エラーメッセージが出力されているエリアの下のほうにあるバーをスクロールさせれば見ることができる) し確認。他にも直接プログラムを実行して結果を調べたり、エラーメッセージの2行目にあるat以下を参考にプログラムを見直す |
無限ループの可能性 | 一定時間内にプログラムが終了しなかった。ループが無限に続いていないか確認 |
機能テストの項目
テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。
項目名 | 詳細 |
---|---|
testM5 | 入力が -5 |
test0 | 入力が 0 |
test1 | 入力が 1 |
test3 | 入力が 3 |
test5 | 入力が 5 |
test10 | 入力が 10 |