(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 ブー!