
乱数とは、その名の通りランダムな数のことです。
要するに、サイコロと同じだと考えて下さい。
ランダムな数が必要になるゲームでは乱数は欠かせません。
また、複雑な現象や統計的な性質の解析などを行う場合には、
乱数を使うことで手軽に実験を行うことが出来ます。
しかし、皆さんもご存じのように、コンピュータは非常に正確な機械であり、
本質的にはランダムに数を作るということは出来ません。
そこで、計算によってランダムな数を得る、疑似乱数という手法が使われます。
疑似乱数では、あくまでも計算によってランダムに見える数を作っています。
[ 疑似乱数 ]
計算によってランダムな数値を得る方法。
本当のランダムではないが、現実的にはランダムだと考えて良い。
[ 疑似乱数の計算 ]
疑似乱数にはさまざまな計算方法があるのですが、
C言語で用意されるのはほとんどが線形合同法です。
詳しい説明は省きますが、簡単に説明すれば、
X = 適当な数 * X(上位ケタの部分を切り捨てて増加を防ぐ)をひたすら繰り返すことで毎回異なる値を得る計算です。
この方法は単純ですがそれほどランダムにはならず、
何回か組み合わせて使うと同じパターンになってしまいます。
しかし、ゲームなどの用途であれば十分ランダムな値になります。

疑似乱数がなんなのかがだいたいわかったところで、
さっそく疑似乱数を使ってみたいと思います。
C言語には、疑似乱数を作るrand関数が用意されています。
なお、rand関数を使うには <stdlib.h> を #include する必要があります。
rand関数には、特にパラメータなどを渡す必要はありません。変数 = rand();
このプログラムの実行結果は、次の通りになるかもしれません。#include <stdio.h> #include <stdlib.h> int main(void) { int i; for (i = 0;i < 10;i++) { printf("%d\n",rand()); } return 0; }
見ての通り、ランダムな値が得られています。
130
10982
1090
11656
7117
17595
6415
22948
31126
9004

前項で、乱数を計算する方法はわかりましたが、
これは数があまりにもバラバラ過ぎて使いにくくなっています。
サイコロの1〜6のように、ある範囲の乱数を得ることはできないのでしょうか。
もし、得られる値の最大値がわかるなら、それを等分してやればよいことになります。
C言語では、rand関数で得られる最大値は RAND_MAX という定数の値でわかります。
従って、rand関数で得られた値をRAND_MAXを等分した値で割れば良いわけですが、
そのための式を計算するのは結構面倒なので、公式を紹介してしまいます。
[ 範囲乱数公式 ]この公式の意味はわからなくてかまいません。
最小値 + (int)( rand() * (最大値 - 最小値 + 1.0) / (1.0 + RAND_MAX) )
このプログラムの実行結果は、次の通りになるかもしれません。#include <stdio.h> #include <stdlib.h> int GetRandom(int min,int max); int main(void) { int i; for (i = 0;i < 10;i++) { printf("%d\n",GetRandom(1,6)); } return 0; } int GetRandom(int min,int max) { return min + (int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); }
見ての通り、1〜6の範囲のランダムな数が得られています。
1
3
1
3
2
4
2
5
6
2
[ 数学とコンピュータ ]
一般にはコンピュータを使うには数学が重要だと思われています。
本質的にはその通りなのですが、そうとも言えない部分があります。
何故なら、コンピュータを使えば計算は自動的にやってくれるわけで、
公式さえ知っていれば、それを当てはめるだけで済んでしまうからです。
前項で作ったGetRandom関数を使えば、好きな乱数を計算することが出来ます。
しかし、実はまだ、考えなければならない問題が残されています。
初めに説明したように、疑似乱数は計算によるものです。
つまり、同じ数を元に作った場合は同じ乱数になってしまうのです。
このことを確認するために、前項で作成したプログラムを2回実行してみます。
1回目
1
3
1
3
2
4
2
5
6
2
なんと、1回目と2回目で全く同じ値が得られています。何回やっても同じです。
2回目
1
3
1
3
2
4
2
5
6
2
この問題を解決するためには、乱数の計算に使う元の数を変える必要があります。
そのための関数として、srand関数が用意されています。
ただし、srand関数を使って別の数値を入れたとしても、srand(元の数);
要するに、srand関数になんとかして完全にデタラメな数を入れたいのですが、
それにピッタリの方法が一つあります。それは、現在時刻を入れる方法です。
秒単位の現在時刻をsrand関数に入れれば、毎回異なる元の数を乱数に使えます。
現在時刻を得る関数はtime関数で <time.h> を #include する必要があります。
srand関数とtime関数を次のように使えば、毎回異なる乱数を計算できます。
乱数発生の場合はtime関数の使い方は知らなくてかまわないのでこの通りにして下さい。srand((unsigned int)time(NULL));
このプログラムを2回実行した結果は次の通りになるかも知れません。#include <stdio.h> #include <stdlib.h> #include <time.h> int GetRandom(int min,int max); int main(void) { int i; srand((unsigned int)time(NULL)); for (i = 0;i < 10;i++) { printf("%d\n",GetRandom(1,6)); } return 0; } int GetRandom(int min,int max) { return min + (int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); }
1回目
6
6
5
4
6
5
1
3
2
6
見事に異なる値が得られるのがわかります。
2回目
5
2
2
4
3
5
4
1
3
1
なお、ここではmain関数でsrand関数を使用していますが、
次のようにすればこれをGetRandom関数に含めることが出来ます。
static変数を使うことで、初めの一回だけsrand関数を使うようにしています。int GetRandom(int min,int max) { static int flag; if (flag == 0) { srand((unsigned int)time(NULL)); flag = 1; } return min + (int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); }