こんにちは、ナナです。
プログラムを作る上で、「動かすたびに毎回違った動きをさせたい」といったニーズは比較的多くあります。
そんな時に使うのが「乱数」と呼ばれる規則性のない数値情報を作り出す仕組みです。
本記事では次の悩みを解消する内容となっています。
では、乱数の作り方を学んでいきましょう。
乱数を作るための標準ライブラリ関数
C言語で乱数を作り出すために必要な標準ライブラリ関数は、次の2つの関数になります。
#include <stdlib.h>
int rand(void);
void srand(unsigned int seed);
各関数の特徴を紹介しましょう。
rand関数の仕様
乱数の値を取得するための関数です。呼び出す度に乱数値を戻り値で取得することができます。
includeファイル | stdlib.h |
関数仕様 | int rand(void); |
引数 | なし |
戻り値 | 0~RAND_MAXの範囲で整数値の乱数を取得できる。 「RAND_MAX」は開発環境により値が異なる。 |
特記事項 | VisualStudio環境では「RAND_MAX」は0x7fffとして定義がされている。 そのため「0~32767」までの範囲でしか乱数を得ることはできない。 実行環境で乱数取得範囲が変わるため、利用の際は注意が必要である。 |
stdlib.h(一部抜粋):Visual Studio環境における定義例
#define RAND_MAX 0x7fff
srand関数の仕様
乱数の種を変更します。種を変更することで、rand関数により取得できる乱数パターンを変更することができます。
includeファイル | stdlib.h |
関数仕様 | void srand(unsigned int seed); |
引数 | seed:乱数の種を指定する。 |
戻り値 | なし |
特記事項 | なし |
C言語で乱数を作り出す時に使う代表的な関数がこの2つになります。特徴を押さえておきましょう。
乱数を得るためのサンプルコードと実行結果
最も基本となるrand関数を利用したサンプルコードと実行結果を示しましょう。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i;
for (i = 0; i < 10; i++)
{
// 乱数値の表示
printf("%d\n", rand());
}
return 0;
}
実行結果は、次のように10個の乱数が表示されます。
41
18467
6334
26500
19169
15724
11478
29358
26962
24464
「0~32767」の整数値の範囲で乱数値が取得できているがわかりますね。これがrand関数の基本的な使い方です。
乱数生成時にrand関数を使う時の注意点
rand関数を使う時は次の点に注意が必要です。先ほどのサンプルコードを2回実行した結果を比較してみましょう。
1回目の実行結果
41
18467
6334
26500
19169
15724
11478
29358
26962
24464
2回目の実行結果
41
18467
6334
26500
19169
15724
11478
29358
26962
24464
全く同じ結果になっていることがわかりますね。つまり、プログラムを再実行した場合、乱数として得られる数値には規則性があるということです。
乱数が抱える問題
rand関数による乱数の生成には「乱数の種」と呼ばれる数値が強く影響しています。rand関数とは「乱数の種」を利用して乱数を生成するからです。
問題は「乱数の種」の数値が同一の場合、rand関数で取得できる乱数の流れが同一になってしまうことです。
このように、乱数を生成するための元となる種が同じ場合、そこから生まれる乱数は毎回同じ流れの数値が生成されてしまうのです。
これでは真の意味で乱数とは言えませんね。この規則性を変えるためには「乱数の種」を変化させる必要があるのです。
プログラムを動かす度に乱数値を変化させたいという要望は普通にあります。「乱数の種」について次は解説しましょう。
乱数の種とは
「乱数の種」とは、その名の通り「乱数という数値の実」を生み出すための種です。乱数の実の数値を変えたければ、乱数の種を変える必要があります。
乱数の種を変更する方法
この「乱数の種」を変更する方法があります。それがsrand関数です。
void srand(unsigned int seed);
srand関数の引数であるseedこそが、変更する「乱数の種」の数値です。
一例として次のようにrand関数を呼ぶ前に、srand関数に「100」という種を設定しました。そうすると、rand関数から取得できる乱数の数が変化します。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i;
// 乱数の種を変更
srand(100);
for (i = 0; i < 10; i++)
{
printf("%d\n", rand());
}
return 0;
}
365
1216
5415
16704
24504
11254
24698
1702
23209
5629
srand関数を呼ばなかった場合と比べると、乱数の値が変化していることがわかりますね。このように乱数の種を変更することで、種から生まれる乱数が変化するのです。
乱数の種の設定は時刻の数値を利用するのがセオリー
ここで新たに困った問題が発生します。「乱数」を変えるために「乱数の種」を変えたいのですが、「乱数の種」の値も規則性を持たせてはならないということです。
つまり、「乱数の種」を変えたいがために、「乱数が必要」という困った状況になるのです。
これを解決するための方法が、乱数の種に時刻情報を指定する方法です。時刻は刻一刻と進んでいくため、乱数的な数値と捉えることができるということです。
time関数の仕様
C言語で時刻を取得するための標準ライブラリ関数です。1970年1月1日からの経過秒数が取得できます。
includeファイル | time.h |
関数仕様 | time_t time(time_t * timer); |
引数 | timer:時刻の出力先。NULLを指定した場合は出力しない。 |
戻り値 | 時刻を返却する。 |
特記事項 | 戻り値と引数は同時刻の情報が出力される。 time_t型は整数型であるが、環境によって範囲が変化する。 |
srand関数に時刻を設定したサンプルコード
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int i;
// time関数を使った乱数の種の設定
srand((unsigned int)time(NULL));
for (i = 0; i < 10; i++)
{
printf("%d\n", rand());
}
return 0;
}
1回目の実行結果
32377
18369
13746
23285
30870
10082
13346
29156
15950
29429
2回目の実行結果
32586
18139
10173
23241
2875
32017
15935
5650
24116
29169
このようにsrand関数に時刻を設定することで、実行するたびに固有の乱数の種を設定することができるのです。
乱数取得のサンプルコード集
それでは、いくつか乱数を取得するサンプルコードを示しましょう。
整数の乱数範囲の取得する公式
皆さんが欲しいと思う整数にはいくつかの取得したパターンがあることでしょう。例えば0~9の範囲、10~20の範囲といった「ここから~ここまで」という取得パターンですね。
この場合、次の式に当てはめることで目的の範囲の乱数を得ることができます。
[取得目標範囲の最小値] + rand()% [乱数で取得したい数の個数]
乱数:「0~9」の整数値を取得するプログラム
0~9の場合は、最小値が「0」であり、取得範囲の数の個数は「10個」です。この場合は次のようにプログラミングします。 最小値の「0」はもちろん省略しても構いません。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int i;
srand((unsigned int)time(NULL));
for (i = 0; i < 10; i++)
{
// 最小値:0 取得個数:10個
printf("%d\n", 0 + rand() % 10);
}
return 0;
}
9
0
4
6
2
8
5
7
6
7
乱数:「5~10」の整数値を取得するプログラム
5~10の場合は、最小値が「5」であり取得範囲の数の個数は「6個」です。個数は「5、6、7、8、9、10」の6個ですよ。次のようにプログラミングします。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int i;
srand((unsigned int)time(NULL));
for (i = 0; i < 10; i++)
{
// 最小値:5 取得個数:6個
printf("%d\n", 5 + rand() % 6);
}
return 0;
}
6
8
8
10
6
6
5
8
9
6
乱数:「-10~3」の整数値を取得するプログラム
負値が含まれている場合も同様の公式で取得可能です。-10~3の場合は、最小値が「-10」であり取得範囲の数の個数は「14個」です。次のようにプログラミングします。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int i;
srand((unsigned int)time(NULL));
for (i = 0; i < 10; i++)
{
// 最小値:-10 取得個数:14個
printf("%d\n", -10 + rand() % 14);
}
return 0;
}
-9
3
-7
-2
1
-10
-9
-1
-6
-8
乱数:「0.0~1.0」の浮動小数点の乱数値を取得するプログラム
rand関数の結果を最大乱数値のRAND_MAXで割ることで算出します。次のようにプログラミングします。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
int i;
srand((unsigned int)time(NULL));
for (i = 0; i < 10; i++)
{
// 0~1の浮動小数点乱数の生成
printf("%lf\n", rand() / (double)RAND_MAX);
}
return 0;
}
0.501968
0.932554
0.696127
0.803461
0.095370
0.453352
0.951170
0.423933
0.563585
0.601032
RAND_MAXは整数型になるのでdouble型やfloat型に明示的キャストを行って算出する必要があります。