2012 前期試験解説


問題1. 基本データ型の8種類と参照型の代表である文字列型の計9種類の型名を整数型、実数型、文字型、論理型、文字列型の5つに分類して次のように表にまとめなさい。 各分類内では、表現できるデータ範囲の広さを不等号を用いて表現しなさい(型 a より型 b の方が広いとき、a<b と書く)。 綴りミス・小文字大文字の怪しいものなど全て×とします。【10点】 (平均:9.1点、満点率:78%)

分類型名
整数型 byte<short<int<long
実数型 float<double
文字型 char
論理型 boolean
文字列型 String

必ず出すよ!と言っていたので、ケアミスでも容赦なく×にして、各分類ごとに2点×5としました。

問題2. 以下の左の処理は2つの整数変数 x, y に入っている値を x ≦ y となるように交換するものです。これを参考にして、3つの整数変数 x,y,z に入っている値を x ≦ y ≦ z となるように交換するプログラムを書きなさい。3つの値をいろいろ変えても対応できるようにすること。答案には、以下の抜けている部分のみを書きなさい。【9点】 (平均:4.6点、満点率:17%)

class Problem02_1 {
    public static void main(String[] args) {
        int x = 7;
        int y = 5;

        if( x > y ) {
            int w = x;
            x = y;
            y = w;
        }
        System.out.println("x=" + x + ", y=" + y );
    }
}
class Problem02_2 {
    public static void main(String[] args) {
        int x = 7;
        int y = 5;
        int z = 3;

        if( x > y ) {
            int w = x;
            x = y;
            y = w;
        }
        if( x > z ) {
            int w = x;
            x = z;
            z = w;
        }
        if( y > z ) {
            int w = y;
            y = z;
            z = w;
        }

        System.out.println("x=" + x + ", y=" + y + ", z=" + z );
    }
}
x=5, y=7 x=3, y=5, z=7

まず、1つの if 文で if( x≦y≦z ) なんて書くのは不可能です(それは問題3でも聴いてますが)。
次に、2つの if 文でも足りないですね。最低3つの if 文が必要です。 上の例では、xとy, xとz, yとz を順に比較していますが、他にも xとy, yとz, xとy と順に比較する方法など、何通りかあります。
順当な並び( x≦y≦z )になっている場合以外の(5種類ある)並び方で場合分けし、それぞれで並び替えするという人が結構いましたが、プログラムとしてはかなりムダですし、数が増えて行ったとき(並び替えプログラム)への応用が利きません。

問題3. 3つの整数変数 x, y, z に入っている値が x ≦ y ≦ z の時にそのことを指す出力文が実行されるように if 文の条件式を答えなさい。【9点】 (平均:7.2点、満点率:63%)

class Problem03 {
    public static void main(String[] args) {
        int x = 3;
        int y = 5;
        int z = 7;
        
        if( x <= y && y <= z ) {
            System.out.println("データは昇順になっています。");
        } else {
            System.out.println("データは昇順になっていません。");
        }
    }
}
データは昇順になっています。

すこしは x <= y <= z と書く人がいるだろうと思っていたら、驚くほど多いので、ショックでした!
あと、& の文字が書き慣れていないのか変だったり、≦ なんて書いてしまったり、注意深く書きましょうね。

問題4. 与えられた n に対して、n×n の整数値行列 m を宣言し、右のような行列となるように (1) から (5) に当てはまる処理を書きなさい。答案には 「(1) これこれ」といった具合に番号も書いて下さい。
最期に (6) に中身のない出力文がありますが、何のためにこれがあるのか(これが無いとどうなるのか)、説明しなさい。【12点】 (平均:8.0点、満点率:20%)

(6) についての説明:printf ではフォーマット指定の中に改行のエスケープシーケースを書かない限りは自動的に改行を行なってはくれない。従って、行列の1行を書き終わった後、つまり2重ループの内側のループが終了したところで、改行のための println() を実行させて次の出力を行頭に移動させている。これがないと、表示は n×n 個の値が1行での表示になってしまう。

class Problem04 {
    public static void main(String[] args) {
        int n = 5;
        int[][] m =  (1) new int[n][n] ;
        
        for( (2) int i=0; i<n; i++) {
            for( (3) int j=0; j<n; j++) {
                if( (4) i == j ) {
                    m[i][j] =  (5) i+1;
                } else {
                    m[i][j] = 0;
                }
            }
        }
        
        for( (2) int i=0; i<n; i++) {
            for( (3) int j=0; j<=n; j++) {
                System.out.printf("%3d", m[i][j]);
            }
            System.out.println(); // (6)
        }
    }
}
 1  0  0  0  0 
 0  2  0  0  0   
 0  0  3  0  0 
 0  0  0  4  0 
 0  0  0  0  5

まず、 n を利用する人が少ないのが残念でした。int[][] m の右辺では使っているのに、(2) や (3) では m.length や m[i].length と一般的な書き方になっています。間違いではないのですが、行列が正方行列であることがアピールされていません。
まだ、行列を int i=1; i<=m.length; i++ などと、1から始める人が複数人いるようです。結構クドく言ったつもりだったけど足りなかったかなあ。
(4) の i == j が i = j になっている人がかなりいて、この間違いは大きいので、それには途中点はやりませんでした。 (6) の説明は、かなり微妙でしたね。分かっている?と思えるけど説明の下手な人が多く、-1 させてもらいました。こうした場合、これが無いとどうなるかを書けば確かですよ。

問題5. 次のプログラムは 100g あたりの値段と、購入した重量、そして消費税率を与えて、消費税込みの購入価格を計算しています。抜けている部分の処理を解答用紙に書きなさい。(購入価格は1円未満を切り捨てして下さい)【10点】 (平均:5.2点、満点率:8%)

もちろん、rate, weight, tax が変わっても正しく計算されるように作ること。

class Problem05 {
    public static void main(String[] args) {
        int rate = 120; // 100g の値段 (円)
        int weight = 410; // 購入した重量 (g)
        int tax = 5; // 消費税率 (%)
        
        int price = (int) ((rate * (double) weight / 100) * (1 + (double) tax / 100)) ;
        System.out.println("値段は " + price + "円です");
    }
}

消費税計算ができない人がかなりいるねえ(税金を引いちゃう人までいて)。 この問題は整数計算の割り算に気付いて (double) のキャストをちゃんとできるか?が1つ目の関門です。100.0 などで割るというのも有りではあるのですが、正解のようにキャストを利用して意味の分かる式にしておきましょう。
2つ目の関門が左辺の整数変数 price へ代入する際の小数点以下の切り捨てをキャストの (int) でちゃんと行なえるか?です。分かってくれた人も結構いたのですが、最も外側のカッコを付け忘れて
 (int) rate * (double) weight / 100 * (1 + (double) tax / 100) などと書いた人が何人かいました。 これでは rate のみ整数化(これってもともと整数)だけで、効力がない。
満点率は低かったですねえ。

問題6. 次のプログラムは、2つの行列 a と b の積を求めるつもりのものですが、間違いだらけです。 間違いを指摘しなさい。 指摘する際に、左の行番号を利用し、その行の正しい書き方を答案用紙に書きなさい。 行を削除もしくは挿入しなければならない、と言う場合も行番号を利用すること。 なお、(文法エラーではないけれど)やるべきでないことも入っています。【10点】 (平均:4.9点、満点率:9%)

全部で11カ所あるので、全部出来てたら11点あげようと思いました。残念ながら11個見つけた人はいなかったです。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
class Problem06 {

    public static void main(String args) {

        double[][] a = {{1.2, -0.3},{5.0, 3.8},{3.3,-1.4}};
        double[][] b = {{0.3, 8.2, -3.7}, {2.5, 9.4,-0.5}};
        
        double[][] c = double[a.length][b.length];
        
        for(int i=0; i<3; i++) {
            for(int j=0; j<3; j++) {
                for(int k=1; k<a.length; k++) {
                    c[i][j] += a[i][j]*b[j][k];
                }
            }
        }
        // この後、行列 c の中身をコンソールに出力して
        // いるはずだが、そこは省略。
    }
}
01
02
03 String args ⇒ String[] args
04
05
06
07
08 右辺: new double[a.length][b[0].length];
09
10 for(int i=0; i<c.length; i++){
11 for(int j=0; j<c[i].length; j++) {
11 と 12 の間に挿入:c[i][j] = 0;
12 for(int k=0; k<a[i].length; k++) {
13 右辺:a[i][k] * b[k][j];

13行目の添字を1つずつ数えて11個です。
10,11,12行目の *.length の部分は意味が同じであれば全て○にしました。例えば、10行目の c.length は、a.length と同じですし、11行目の c[i].length は c[0].length でも b[0].length でも OKです。ただし、これを a.length とか b[i].length とかにするのは×です。チェックしてみて下さい。

問題7. 次のように初期値で与えられた行列 a に対して、a と a の転置行列の積(a・ta)を計算し(行列 b に値を格納)、結果をコンソールに出力するプログラムを完成させ、メールで送りなさい。
くどいですが、データを変えても正しく動くプログラムとすること。【10点】(平均:5.1点、満点率:16%)

package test;

class Problem07 {

    public static void main(String[] args) {
        int[][] a = {{1,2},{-2,3},{0,4}};

        int[][] b = new int[a.length][a.length];

        for(int i=0; i<b.length; i++) {
            for(int j=0; j<b[i].length; j++} {
                b[i][j] = 0;
                for(int k=0; k<a[i].length; k++) {
                    b[i][j] += a[i][k] * a[j][k];
                }
            }
        }

        for(int i=0; i<b.length; i++) {
            for(int j=0; j<b[i].length; j++) {
                System.out.printf("%3d",b[i][j]);
            }
            System.out.println();
        }
    }
}
  5  4  8
  4 13 12 
  8 12 16 

まず、配列 a, b 以外の配列を使わないで計算してほしい!と試験開始時に言ったのですが、何人かは逆行列のための配列 at を別に用意し、2つの行列 a と at の積を計算するという方法を使いました。で、それに目をつむって、さて出来ているのか?というと、出来ていないことの方が多く、後期が心配です。

行列のかけ算は、結果の行列の (i,j) 成分が1つ目の行列の i 行目と2つ目の行列の j 列目の積になりますが、この場合、2つ目の行列が1つ目の行列の転置ですから、2つ目の行列の j 列目というのは、1つ目の行列の j 行目に対応します。 結果、(i,j) 成分の値は、行列 a の i 行目と j 行目を掛ければ良いことがわかります。

ここでは、でこぼこな配列は考えていないので、プログラム中の a[i].length や b[i].length は、 a[0].length や b[0].length と固定しても構わないでしょう。

問題8. 次のプログラムは、ある課目の試験の成績集計のプログラムです。指示に従ってプログラムを完成し、メールで送りなさい。【10点】(平均:3.2点、満点率:6%)

整数配列 score:試験の点数が入っている
文字列配列 rank:集計結果の出力の際に用いる文字列
整数配列 border:成績のランク(S,A,B,C,D)の最低点(将来変わるかもしれないので)
整数配列 count:各ランクの人数を記憶するため

各点数がどのランクに含まれるかは、border に格納されている各ランクの最低点との比較でできます。どのランクに含まれるか分かったら、それに対応する count[i] の値を1増やしてあげて、集計をとります。
最初の出力は、rank の文字列を利用し、それに集計結果の count[i] の値を書いて作っています。
後半の出良くは、同じく rank の文字列の後に、各ランクの人数 count[i] の回数だけ文字 * を出力することで作っています。(後期ならグラフィックスを使うのですが、ここでは文字を使って棒グラフを作ります)

package test;

class Problem08 {

    public static void main(String[] args) {
        int[] score = {100,92,65,75,80,88,95,50,52,100,85,76,82,78,95};
        String[] rank = {"S[100,90]:","A (90,80]:","B (80,70]:",
                                      "C (70,60]:","D (60, 0]:"};
        int[] border = {90,80,70,60,0};
        int[] count = new int[border.length];
        
        for(int i=0; i<count.length; i++) {
            count[i] = 0;
        }
        
        // 集計
        for(int i=0; i<score.length; i++) {
            for(int j=0; j<border.length; j++) {
                if( score[i] >= border[j] ) {
                    count[j]++
                    break;
                }
            }
        }

        // 出力その1
        for(int i=0; i<count.length; i++) {
            System.out.println(rank[i] + count[i]);
        }
        System.out.println();

        // 出力その2
        for(int i=0; i<count.length; i++) {
            System.out.print(rank[i]);
            for(int j=0; j<count[i]; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}
 S[100,90]:5
 A (90,80]:4
 B (80,70]:3
 C (70,60]:1
 D (60, 0]:2

 S[100,90]:***** 
 A (90,80]:****
 B (80,70]:***
 C (70,60]:*
 D (60, 0]:**

やることは分かっているんだけど、それをプログラムに出来ない、というモドカシさをかなりの人が味わった問題になりました。border という配列の使い方が分からない人が多く、結局 border を無視して、独自にランク判定をしている人が多かったですね。また、配列の利用ということでは、まだまだ経験不足ということか、データが変更されたら途端に破綻するプログラムが多かったです。後期また頑張りましょう。


jmwada@ed.tus.ac.jp