2002 年度「計算機基礎論3B」 2002-12-06

練習問題(前回の補足)

次のプログラムは、関数 int swap(int x, int y) を書いて xy の値を入れかえようとしたものだが、 うまく働かない。なぜか?

#include <stdio.h>

int swap(int x, int y);

main()
{
    int x, y;

    x = 1; y = 2;
    swap(x, y);
    printf("%d %d\n", x, y);
}


int swap(int x, int y)
{
    int tmp;

    tmp = x; x = y; y = tmp;
}
なお、 今まで習った範囲の知識では二つの変数の値を入れかえる関数は書けない。 なぜか。少し考えてみよ。

乱数で遊ぼう

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

main()
{
    int i;

    srand((unsigned int)time(NULL));    /* 乱数の種の初期化 */

    for (i = 1; i <= 10; i++) {
        printf("%d ", rand());          /* 乱数を使う */
    }
    printf("\n");
    printf("この乱数の最大の値は %d です.\n", RAND_MAX);
}

「乱数の種の初期化」の行。 関数 time() は現在時刻を返す関数、 関数 srand() は乱数の種の初期化を行なう関数である。 「乱数を使う」の行。 関数 rand() は 0 から RAND_MAX までの整数に値をもつ乱数を返す。

time() については K&R2 §B10 に、 srand()rand() については §7.8.7 などに、 型変換 (unsigned int) については §2.7 に書かれているが、 いまのところは上の形のまま決まり文句と思っておけばよい。 §2.7 の rand(), srand() の定義例は、自分でこれらの関数を書くとしたらこんなふうになる、 ということであり、普通は自分で書く必要はない。 time() を使うには #include <time.h> が、 rand(), srand() を使うには #include <stdlib.h> が必要だが、 このことは K&R2 の付録 B を見ればわかる。 #include たちの順序はどうでもよいが、 #include <stdio.h> は最初に書くのが普通のようだ。

rand() の返す乱数はあまりよい乱数ではないらしいが、 その点についてはここでは考えないことにしよう。 つまり、この関数の返すものは乱数であるとみなそう。

練習問題

上のプログラムから「乱数の種の初期化」の行を取り除いたらどうなるか。

上のプログラムで、「乱数の種の初期化」の行を間違って for ループの中に入れたらどうなるか。

上のプログラムを改造し、正の整数 n に対し 0 から n までの整数に値をもつ乱数を出力するようにせよ。 0 から n まで、どの整数も同じ確率で出るように工夫するか、 それとも適当なところで妥協するか、考えてみよ。

乱数を利用し、 小学生に暗算の練習問題を出し、結果を採点してやるプログラムを書け。

20 個ぐらいの int 型の配列を用意し、 それらを乱数で初期化し、 それらの中で最も大きいものを探し出して 「最大なのは 3 番目の 253 です」 のように答えるプログラムを書け。

(double) rand() / (RAND_MAX + 1.0) とすれば、 区間 [0, 1[ に値を持つ double 型の乱数が得られる。 これを二つ組み合わせると、 正方形 [0, 1[ × [0, 1[ の中の点をランダムに選んだことになる。 その点と原点との距離が 1 以下である確率は π/4 である。 このことを利用して π の近似値を実験的に求めるプログラムを書け。 (K&R2 §7.8.7 では RAND_MAX+1 となっているが、 これは間違いである。)

トランプの一人遊びのゲームを作れ。 ヒント: トランプは一組 52 枚、ジョーカーを入れても 53 枚である。 0 から 51 までの整数に対し、 13 で割った余りが A, 2, 3, ... , 10, J, Q, K の別を、 商がスペード、ハート、クラブ、ダイヤの別を表す、 などとしてトランプのカードを対応させるとよい。


岩瀬順一