乱数(ランダム値)の生成

プログラムでランダムな値を得る

rand関数

プログラミングではランダムな値が欲しい場合が時々あります。
そのような場合にはrand関数を使用します。


#include <stdio.h>
#include <stdlib.h>

int main()
{
    int rnd;

    for (int 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 (int 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 (int 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を掛ければ得ることができます。