(33.1) 「乱数」が数学でどう定義されるかは、ここでは考えない。 計算機でいう乱数とは、 種(たね)と呼ばれる整数を元に計算を行なって発生させた、 乱数のように見える数列のことである。
(33.2) 関数 rand() は、呼ばれるたびに、 0 以上 RAND_MAX 以下の整数(int 型)に値をとる乱数の項を、 一つずつ返す。 次のプログラム
#include <stdio.h> #include <stdlib.h> /* rand() */ main() { int i; for (i = 0; i < 10; i++) { /* 10 項からなる乱数列を発生 */ printf("%d\n", rand()); } }は 10 個の整数を出力するが、これらの並びが乱数、というわけである。
(33.3) RAND_MAX はコンパイラごとに違う値を取り得る定数である。 その値は次のプログラムでわかる。
#include <stdio.h> #include <stdlib.h> /* RAND_MAX */ main() { printf("%d\n", RAND_MAX); }
(33.4) (33.2) のプログラムは、毎回同じ乱数列を出力する。 それでは困るので、現在時刻を乱数の種(たね)にすることを考える。
#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()); } }このコンパイラでこのプログラムを動かすと、 現在時刻の秒未満は無視されるようである。
(33.5) 種の値を出力させるのは、再現性のためである。 すなわち、 このプログラムが出力した種を次のプログラム (乱数の種の設定の部分以外は省略)に入力すれば、 全く同じ乱数列が得られる。
{ /* この中カッコの中(乱数の種の設定)はわからなくてもよい */ unsigned seed; printf("乱数の種を入力してください.\n"); scanf("%u", &seed); srand(seed); /* それを乱数の種に */ }
(33.6) rand() % 100 とすると、 0 以上 100 未満の整数に値をとる乱数が得られる。 厳密に言えば 0 の出現率が 99 のそれよりわずかながら高いが、 この授業ではそこまでは気にしないことにしよう。
(33.7) rand() / (1.0 + RAND_MAX) とすると、 区間 [0, 1[ に値をとる乱数になる。 1 でなく 1.0 と書くのは、 加法を int 型でなく double 型の中で行なうためであるが、 この授業ではこのあたりの問題には深入りしないので、 全体で“決まり文句”として理解しておけばよい。
(33.8) それを使って円周率の近似値を求めるプログラム。 x, y は [0, 1[ に値を持つ乱数になる。 これらを x 座標 y 座標に持つ点を平面上にとると、 それは 1×1 の正方形の中のランダムな点となる。 その点と原点との距離が 1 以下になる確率は四分の一円の面積 π/4 に等しいから、 N 回のうち p 回だけその四分の一円にはいったとすると π の近似値として 4*p/N が得られる。 (プログラム内で 4.0 * p / N としているのは、 p も N も int 型なのでこうしないと割り算の結果が int 型になってしまうためである。 よって printf("... %f です.\n", 4 * p / N); は正しいプログラムでないし、 printf("... %d です.\n", 4 * p / N); では答えは「3」になる。)
#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); }
(33.9) ユーザにランダムな数を与えて計算問題をさせるプログラムも書ける。 例えばこんな感じ。
33 + 14 = 47 正解です! 44 + 29 = 63 ブー!