2006 年度「計算機基礎論3B」 2006-11-17

§24 乱数

(24.1) 「乱数」が数学でどう定義されるかは、ここでは考えない。 計算機でいう乱数とは、 種(たね)と呼ばれる整数を元に計算を行なって発生させた、 乱数のように見える数列のことである。

(24.2) 関数 rand() は 0 以上 RAND_MAX 以下の整数 (int 型)に値をとる乱数を返す。 RAND_MAX の値は次のプログラムでわかる。 実験したらメモしておこう。 「RAND_MAX の値は               である。」 (コンパイラごとに違う値を取り得る定数である。)

#include <stdio.h>
#include <stdlib.h> /* RAND_MAX */

main() {
    printf("%d\n", RAND_MAX);
}

(24.3) 乱数は次のようにして使うが、 これでは何度起動しても毎回同じ乱数列になってしまう。

#include <stdio.h>
#include <stdlib.h> /* rand() */

main() {
    int i;

    for (i = 0; i < 10; i++) {      /* 10 項からなる乱数列を発生 */
        printf("%d\n", rand());
    }
}

(24.4) そこで、現在時刻を乱数の種(たね)にする。

#include <stdio.h>
#include <stdlib.h> /* rand() */
#include <time.h>   /* time() */

main() {
    int i;

    {   /* この中カッコの中(乱数の種の設定)はわからなくてもよい */
        unsigned seed = (unsigned)time(NULL);   /* 現在時刻を取得して */
        printf("乱数の種は %u です.\n", seed);
        srand(seed);                            /* それを乱数の種に */
    }

    for (i = 0; i < 10; i++) {
        printf("%d\n", rand());
    }
}
このCコンパイラでこのプログラムを動かすと、 現在時刻の秒未満は無視されるようである。

(24.5) 種の値を出力させるのは、再現性のためである。 すなわち、 このプログラムが出力した種を次のプログラム (乱数の種の設定の部分以外は省略)に入力すれば、 全く同じ乱数列が得られる。



    {   /* この中カッコの中(乱数の種の設定)はわからなくてもよい */
        unsigned seed;

        printf("乱数の種を入力してください.\n");
        scanf("%u", &seed);
        srand(seed);                            /* それを乱数の種に */
    }

(24.6) rand()rand() % 100 で置き換えれば、 0 以上 100 未満の整数に値を持つ乱数が得られる。 厳密に言えば、 これでは 0 の出現率が 99 のそれよりわずかながら高いし、 関数 rand() がうまく作られていないため十分にランダムな数列が得られないという話もきくが、 この授業ではそこまでは気にしないことにしよう。

(24.7) 乱数を使って円周率の近似値を求めるプログラム。 x, y は [0, 1[ に値を持つ乱数になる。 これらを x 座標 y 座標に持つ点を平面上にとると、 それは 1×1 の正方形の中のランダムな点となる。 その点と原点との距離が 1 以下になる確率は四分の一円の面積 π/4 に等しいから、 N 回のうち p 回だけその四分の一円にはいったとすると π の近似値として 4*p/N が得られる。 (プログラム内で 4.0 * p / N としているのは、 pNint 型なのでこうしないと割り算の結果が int 型になってしまうためである。 よって printf("... %f です.\n", 4 * p / N); は正しいプログラムでないし、 printf("... %d です.\n", 4 * p / N); では答えは「3」になる。 1.0 + RAND_MAX の説明は省略する。)

#include <stdio.h>
#include <stdlib.h> /* rand() */
#include <time.h>   /* time() */

#define N 10000

main() {
    int i, p;
    double x, y;

    {   /* この中カッコの中(乱数の種の設定)はわからなくてもよい */
        unsigned seed = (unsigned)time(NULL);   /* 現在時刻を取得して */
        printf("乱数の種は %u です.\n", seed);
        srand(seed);                            /* それを乱数の種に */
    }

    p = 0;
    for (i = 0; i < N; i++) {
        x = rand() / (1.0 + RAND_MAX);  /* 0 <= x < 1 を満たす乱数 */
        y = rand() / (1.0 + RAND_MAX);
        if (x*x + y*y <= 1) {
            p++;
        }
    }
    printf("円周率の近似値は %f です.\n", 4.0 * p / N);
}

(24.8) トランプを一枚ずつひいてその数で競うゲームを作るには、 rand() % 13 + 1 とすればよい。 これで、1 以上 13 以下の整数に値を持つ数列が得られる。

(24.9) ユーザにランダムな数を与えて計算問題をさせるプログラムも書ける。 例えばこんな感じ。

33 + 14 = 47
正解です!
44 + 29 = 63
ブー!


岩瀬順一