この記事の要点
- 乱数を生成する際は、rand関数を利用する
- 乱数の種をばらつかせることで、常に同じ乱数パターンになることを防ぐ
- time関数とsrand関数を組み合わせて、乱数の種を変更する
以前、C言語は2位のシェアを持つプログラミング言語で、歴史が長く安定して案件があるとご紹介していました。
今回は、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関数では以下のように乱数を生成しています。
↓
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言語ならメモリが少ないハードでもゲーム開発は可能なので、参考にしてください。