乱数(ランダム値)の生成
プログラムでランダムな値を得る
rand関数
プログラミングではランダムな値が欲しい場合が時々あります。
ランダム値はrand
関数で生成できます。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int rnd;
for (size_t i = 0; i < 10; i++)
{
rnd = rand();
printf("%d\n", rnd);
}
getchar();
}
rand
関数を使用するには#include <stdlib.h>
が必要です。
rand
関数に引数はありません。
戻り値はint型で、0からRAND_MAX
という定数までのランダム値を返します。
RAND_MAX
はVisual Studio 2015では「32767」と定義されているようです。
srand関数
上記のサンプルコードは、実は乱数の生成としては不十分です。
プログラムを実行してみると上手くランダム値が生成されているように見えますが、一度終了してから再度プログラムを実行しても同じ値が表示されるはずです。
rand
関数による乱数の生成は疑似乱数といって、ランダムに「見える」値を返しているに過ぎません。
疑似乱数は、乱数の「種(seed)」に従って値を生成しており、この種が同じである限り同じ値を生成します。
プログラム実行ごとに乱数の種を別のものに変更することで、ランダムな値を得ることができます。
それにはsrand
関数を使用します。
乱数の種(srand
関数の引数)には一般的に現在の時刻を使用します。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int rnd;
//乱数の種に現在の時刻を指定
srand((unsigned)time(NULL));
for (size_t i = 0; i < 10; i++)
{
rnd = rand();
printf("%d\n", rnd);
}
getchar();
}
現在の時刻を得るためにtime
関数を使用します。
time
関数の引数にNULLを指定すると、1970年1月1日からの秒数を返します。
time
関数は#include <time.h>
が必要です。
日時について詳しくは日時の取得を参照してください。
srand
関数の引数にそのままtime
関数の戻り値を指定することで、プログラムが起動する毎に異なる乱数の種をセットすることができます。
srand
関数はrand
関数を使用する前に一度だけ呼び出せばOKです。
ループに注意
srand
関数を以下のようにループ内で何度も呼び出すと、常に同じ値しか得られない可能性があります。
for (size_t i = 0; i < 10; i++)
{
srand((unsigned)time(NULL));
printf("%d\n", rand());
}
ループ処理というのはよほど重たい処理でなければ一回のループに1秒もかかりませんから、何度も同じ値(秒数)で乱数の初期化をすることになります。
その結果、同じ値しか取得できなくなるというわけです。
関数内などにsrand
関数を組み込むと、意図しないところで何度もsrand
関数の呼び出しが行われてしまうことがあるので気を付けましょう。
rand
関数により得られる乱数は精度の悪い乱数です。
一般的なプログラムであれば問題ありませんが、研究目的などで高い精度の乱数が必要な場合には向きません。
特定の範囲のランダム値を得る
0~32767の間のランダム値を得ても、そのままではあまり実用的ではありません。
プログラム中で必要な範囲のランダム値を得るには以下のようにします。
srand((unsigned)time(NULL));
int rndInt;
//1~6のランダム値を得る(整数)
rndInt = rand() % 6 + 1;
//0~99のランダム値を得る(整数)
rndInt = rand() % 100;
double rndReal;
//0~1以下のランダム値を得る(小数)
rndReal = (double)rand() / RAND_MAX;
//0~1未満のランダム値を得る(小数)
rndReal = (double)rand() / (RAND_MAX + 1);
//0~10未満のランダム値を得る(小数)
rndReal = (double)rand() / (RAND_MAX + 1) * 10;
例えばサイコロならば1~6の値が欲しいので、rand
関数で得られた値を6で割った余りに1を足せば望みの範囲のランダム値を得ることができます。
0~1の間の値は定数RAND_MAX
で割ることで得られます。
ただし、rand関数の戻り値か定数RAND_MAX
かのどちらかを目的の小数型(double、float)でキャストする必要があります。
0~10の間、かつ小数を含む値が欲しい場合は、いったん0~1未満のランダムな小数値を生成してから10を掛ければ得ることができます。