C言語での乱数の生成と扱い方【randとsrandと乱数の種】

この記事の要点

  • 乱数を生成する際は、rand関数を利用する
  • 乱数の種をばらつかせることで、常に同じ乱数パターンになることを防ぐ
  • time関数とsrand関数を組み合わせて、乱数の種を変更する

以前、C言語は2位のシェアを持つプログラミング言語で、歴史が長く安定して案件があるとご紹介していました。

C言語での副業はなぜ難しい?業務の種類と必要なスキルからわかるその理由

今回は、C言語の乱数の生成について解説します。

C言語で乱数を生成する際は、「rand関数」を利用します。

乱数の種をばらつかせることで、常に同じ乱数パターンになることを防ぐことができるので、「time関数」と「srand関数」を組み合わせることがポイントです。

また、そもそも乱数とは?乱数の種とは?といった初心者向けの解説も丁寧に行っています。

C言語を使ってゲームを作ってみたい初心者の方には、必見の内容です。

乱数とは

乱数とは、ランダムな数字のことです。

乱数は、ゲームなどでよく使われます。

例えば、敵に与えるダメージや受けるダメージ、フィールドでエンカウントする敵の種類などをばらつかせるために用いられます。

しかし、コンピュータで完全な乱数を作ることはできません

「入力が同じなら、出力も毎回同じ」というのが、コンピュータの大前提だからです。

そこで、完全な乱数の代わりに、計算によって「ランダムに見える数字」を生成します。

これを疑似乱数と呼びます。

当記事の「乱数」というワードは、全て疑似乱数のことです。

rand関数

多くのプログラミング言語では、乱数を作るためのライブラリが存在します。

C言語では、rand関数が用意されています。

rand関数の仕様は、以下の通りです。

概要0~RAND_MAXの範囲の疑似乱数を生成し、返す
includeするヘッダstdlib.h
形式int rand(void);
引数なし
戻り値0~RAND_MAXの範囲の整数
※RAND_MAXの値は環境により異なる

rand関数を使ったサンプルと、実行結果はこちらです。

サンプルコード

#include <stdio.h>
#include <stdlib.h>
int main(void){
    printf("RAND_MAXの値:%d\n", RAND_MAX);
    for( int i = 0; i < 10; ++i ){
        printf( "%d\n", rand() );
    }
    return 0;
}

実行結果

RAND_MAXの値:2147483647
1804289383
846930886
1681692777
1714636915
1957747793
424238335
719885386
1649760492
596516649
1189641421

こちらの環境では乱数の最大値であるRAND_MAXは「2147483647」でした。

実行結果で得られた乱数は、「0~2147483647」の範囲の値になっています。

しかし、実はこのプログラムには1つ問題があります。

それは、何度実行しても、毎回同じ乱数のパターンになるという点です。

こちらの環境で、このプログラムを2回目に実行した結果がこちらです。

2回目の実行結果

RAND_MAXの値:2147483647
1804289383
846930886
1681692777
1714636915
1957747793
424238335
719885386
1649760492
596516649
1189641421

1回目と全く同じパターンです。

これは、次に説明する「乱数の種」が変わっていないからです。

乱数の種とは

乱数の種(シードとも呼ばれます)とは、乱数を計算する際の元になる値です。

詳細は省きますが、rand関数では以下のように乱数を生成しています。

1回目の呼び出し:乱数の種を初期値として、ある計算を行い、乱数を生成する。

2回目の呼び出し:得られた乱数に対して同じ計算を行い、別の乱数を生成する。

3回目の呼び出し:得られた乱数に対して同じ計算を行い…(以下繰り返し)

そのため、乱数の種が同じなら、乱数のパターンは毎回同じになります。

つまり、乱数をばらつかせるためには、乱数の種を毎回変化させる必要があります。

time関数

よく使われるのは、現在の日時を利用するテクニックです。

C言語では、標準ライブラリのtime関数で現在の日時情報を取得できます

time関数の仕様は、以下の通りです。

概要1970年1月1日からの経過秒数を返す
includeするヘッダtime.h
形式time_t time(time_t * timer);
引数得られた日時情報の格納先(NULLの場合、格納しない)
戻り値得られた日時情報

time関数で得た日時情報を乱数の種とすることで、同一日時に実行されない限りは、毎回異なる乱数パターンを得られます。

srand関数

次に、乱数の種を指定する方法について説明します。

C言語では、乱数の種を指定する際にはsrand関数を使います。

srand関数の仕様は、以下の通りです。

概要rand関数で使う乱数の種を変更する
includeするヘッダstdlib.h
形式void srand(unsigned int seed);
引数乱数の種とする値
戻り値なし

サンプルと、実行結果はこちらです。

サンプルコード

#include <stdio.h>
#include <stdlib.h>
int main(void){
    unsigned int now = (unsigned int)time(NULL);
    printf("乱数の種:%d\n", now);
    srand(now);
    for( int i = 0; i < 10; ++i ){
        printf( "%d\n", rand() );
    }
    return 0;
}

1回目の実行結果

乱数の種:1665923559
89276058
696401342
623232829
1410674853
1959107951
1868423341
880103630
160157705
605191914
1341827754

2回目の実行結果

乱数の種:1665923584
1179583
320351406
1834644553
1415123332
559976261
833328016
2006943341
961421594
893285590
1590576049

今回は、乱数の種に現在の日時を利用しているため、1回目と2回目で生成される乱数のパターンが変わっています

範囲指定して乱数を生成する方法

実際には、「0~10までの範囲でランダムな値」など、範囲指定して乱数を生成したい場合がほとんどです。

範囲指定してランダムな整数を生成するサンプルコードをご紹介します。

サンプルコード

#include <stdio.h>
#include <stdlib.h>
int getRandomNumber(int min, int max){
    return rand() % (max - min + 1) + min;
}
int main(void){
    unsigned int now = (unsigned int)time(NULL);
    srand(now);
    for( int i = 0; i < 10; ++i ){
        printf( "%d\n", getRandomNumber(0, 10) );
    }
    return 0;
}

使いやすいように、乱数生成処理は関数化しています。

第一引数に乱数の最小値、第二引数では乱数の最大値を指定します。

範囲指定には、「どんな自然数でも、自然数nで割った余りは0~n-1の範囲になる」ことを利用しています。

まとめ

今回は、乱数の生成について解説しました。

乱数生成は、RPGなどのゲームを制作する場合にはほとんど必須の処理です。

しかし、rand関数はよく仕様を調べてから使わないと、毎回乱数のパターンが同じになってしまうため、注意が必要です。

乱数生成する方法は、忘れたらまた調べればよいだけなので暗記する必要はありません。

コンピュータで使えるのは疑似乱数であることや、異なるパターンの乱数を生成するために乱数の種を変更する必要があることなど、根本的な知識はしっかり身に着けておきましょう。

また、今後ゲーム開発をしていきたい方に向けて、C言語でゲーム制作をするための具体的なステップや、便利なライブラリについて解説しています。

C言語ならメモリが少ないハードでもゲーム開発は可能なので、参考にしてください。

初心者にもできる!C言語でのゲーム制作入門