乱数って使ってますか?ある範囲の数値の中から偏りなく無作為に数値を選んで使いたい場合に乱数を使用します。自動で繰り返し処理を行いたい場合などに使う数値として乱数はよく使われます。
例えばパスワードなどでは乱数を使って無作為に英文字や数値を選んで並べたものが使われています。
この記事では、乱数について
- 乱数とは
- randの使い方
- randを使ったサンプルコード
など基本的な内容から、具体的な使い方の内容についても解説していきます。今回は乱数について、使い方をわかりやすく解説します!
乱数とは
乱数とは一意的ではなくランダムで何が出るかわからない数字のことです。C言語ではrand関数を使ってよく疑似乱数を生成させます。
プログラミングで生成する乱数の羅列は、事前に乱数のシード(乱数を作成するときの最初の設定値)がわかれば同じ列を再現できます。しかし、一般的にシードが分からない場合、乱数の次の値を先読みは困難です。
randの使い方
rand関数を使って乱数を発生させる方法についてみていきましょう。
基本的な使い方
rand関数を使うためにはヘッダーファイル「stdlib.h」をインクルードする必要があります。rand関数を使うと0~RAND_MAXまでの整数の乱数を生成することができます。RAND_MAXはヘッダーファイル「stdlib.h」で定義されています。
RAND_MAXの値は環境により異なりますので注意しましょう。この解説では下記のように定義されている環境で確認しています。
#define RAND_MAX 0x7fffffff
0x7fffffffは十進数で表すと2147483647となります。
したがって0~2147483647までの乱数を発生させることになります。rand関数は引数は指定しません。
それではサンプルコードで使い方をみていきましょう。
#include <stdio.h> #include <stdlib.h> int main(void) { printf("RAND_MAX: %d\n", RAND_MAX); // 0から2147483647までの乱数を発生 for(int i = 0; i < 3; i++) { printf("%d\n", rand()); } // 1から10までの乱数を発生 for(int i = 0; i < 3; i++) { printf("%d\n", rand() % 10 + 1); } return 0; }
実行結果:
RAND_MAX: 2147483647 1804289383 846930886 1681692777 6 4 6
このサンプルコードでは3回繰り返して乱数を発生させています。乱数を発生させていますので、環境を変えて実行した結果はこの結果とは異なる表示になります。
この環境ではrand関数は0から2147483647までの乱数を生成しますので、例えば1~10までの乱数を生成したい場合は10の剰余に1加えて1~10の数値を算出します。なお、このサンプルコードでは、初期化を行わない場合、繰り返し実行しても同じ結果が出力される点には注意が必要です。異なる乱数列を得るには、srand関数を使い初期化するのを推奨します。
初期化の方法について詳しくみていきましょう!
初期化の方法
初期化を行うにはsrand関数を使用します。
srand関数の引数にはunsigned int型の整数を使用します。符号なし整数に同じ値を使うと乱数は同じ値で初期化されます。乱数の初期値を毎回変えたい場合は、一般にtime関数を使って現在の時刻を取得して利用します。
time関数は現在の歴時刻を秒単位で取得します。ちなみに歴時刻とはグリニッジ標準時の1970年1月1日0時から現在までの経過時間を秒単位で表したものです。time関数を使うためにはヘッダーファイル「time.h」をインクルードする必要があります。time関数の引数にNULLを指定して使用します。
それではサンプルコードで確認していきましょう。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> int main(void) { for(int i = 0; i < 3; i++) { srand(1); // 乱数の初期化 printf("%d回目の乱数発生: ", i + 1); // 0から2147483647までの乱数を発生 for(int j = 0; j < 3; j++) { printf("%d, ", rand()); } printf("\n"); } for(int i = 0; i < 3; i++) { srand((unsigned int)time(NULL)); // 現在時刻の情報で初期化 printf("%d回目の乱数発生: ", i + 1); // 0から2147483647までの乱数を発生 for(int j = 0; j < 3; j++) { printf("%d, ", rand()); } sleep(1); // time関数で取得する時刻を変えるために1秒待機 printf("\n"); } for(int i = 0; i < 3; i++) { srand((unsigned int)time(NULL)); // 現在時刻の情報で初期化 printf("%d回目の乱数発生: ", i + 1); // 1から10までの乱数を発生 for(int j = 0; j < 3; j++) { printf("%d, ", rand() % 10 + 1); } sleep(1); // time関数で取得する時刻を変えるために1秒待機 printf("\n"); } return 0; }
実行結果:
1回目の乱数発生: 1804289383, 846930886, 1681692777, 2回目の乱数発生: 1804289383, 846930886, 1681692777, 3回目の乱数発生: 1804289383, 846930886, 1681692777, 1回目の乱数発生: 1004092231, 3888874, 662954966, 2回目の乱数発生: 1779225463, 899014191, 250342318, 3回目の乱数発生: 1467717538, 1774429879, 1966374531, 1回目の乱数発生: 5, 8, 7, 2回目の乱数発生: 3, 8, 7, 3回目の乱数発生: 9, 9, 7,
このサンプルコードでは、srand関数を使ってrand関数を初期化しています。まずsrand関数の引数に1を指定して初期化しrand関数で乱数を発生させた場合は、3回とも同じ乱数が生成されています。次にsrand関数の引数にtime関数を使って初期化しrand関数で乱数を発生させた場合は、3回とも違う値の乱数が生成されています。
なお、time関数は秒単位で歴時刻を取得しますので繰り返し処理が1秒以内で終了する場合は同じ歴時刻を取得します。
そうすると同じ値で初期化することになりますので、このサンプルコードではsleep関数を使って1秒待機させ違う歴時刻を取得するようにしています。sleep関数を使うにはヘッダーファイル「unistd.h」をインクルードする必要があります。
randを使ったサンプルコード
rand関数を使って乱数を生成するサンプルコードとして、サイコロの出目を表示する例とじゃんけんの勝ち負けを表示する例をご紹介します。
サイコロ
このサンプルコードではrand関数を使って1~6までの乱数を生成し出力表示しています。
srand関数を使用すると、現在のUNIXエポック時間(1970年1月1日0時からの経過時間)で初期化されるため、実行するたびに出力表示が変わる可能性があります。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand((unsigned int)time(NULL)); // 現在時刻の情報で初期化 for(int i = 0; i < 3; i++) { printf("%d回目: ", i + 1); // 1から6までの乱数を発生 int num = rand() % 6 + 1; switch(num) { case 1: printf("一の目が出ました\n"); break; case 2: printf("二の目が出ました\n"); break; case 3: printf("三の目が出ました\n"); break; case 4: printf("四の目が出ました\n"); break; case 5: printf("五の目が出ました\n"); break; case 6: printf("六の目が出ました\n"); break; default: printf("numの値が不正です\n"); } } return 0; }
実行結果:
1回目: 四の目が出ました 2回目: 二の目が出ました 3回目: 三の目が出ました
じゃんけん
このサンプルコードではrand関数を使って0~2までの乱数を生成しています。0の場合はグー、1の場合はチョキ、2の場合はパーとしてじゃんけんの勝ち負けの出力表示処理をしています。
こちらも、前で紹介した「サイコロ」と同様に現在のUNIXエポック時間で初期化されるため、実行するたびに出力表示が変わる可能性があります。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand((unsigned int)time(NULL)); // 現在時刻の情報で初期化 for(int i = 0; i < 3; i++) { printf("%d回目: ", i + 1); // 0から2までの乱数を発生 int num1 = rand() % 3; int num2 = rand() % 3; if(num1 == num2) { if(num1 == 0) { printf("Aはグーで、Bもグー。引き分け。\n"); } else if(num1 == 1) { printf("Aはチョキで、Bもチョキ。引き分け。\n"); } else { printf("Aはパーで、Bもパー。引き分け。\n"); } } else if(num1 == 0) { if(num2 == 1) { printf("Aはグーで、Bはチョキ。Aの勝ち。\n"); } else { printf("Aはグーで、Bはパー。Bの勝ち。\n"); } } else if(num1 == 1) { if(num2 == 0) { printf("Aはチョキで、Bはグー。Bの勝ち。\n"); } else { printf("Aはチョキで、Bはパー。Aの勝ち。\n"); } } else if(num1 == 2) { if(num2 == 0) { printf("Aはパーで、Bはグー。Aの勝ち。\n"); } else { printf("Aはパーで、Bはチョキ。Bの勝ち。\n"); } } } return 0; }
実行結果:
1回目: Aはパーで、Bもパー。引き分け。 2回目: Aはグーで、Bはチョキ。Aの勝ち。 3回目: Aはグーで、Bはパー。Bの勝ち
まとめ
ここでは、乱数の生成について説明しました。
rand関数を使って乱数を生成する場合、初期化せずに実行すると同じ乱数が生成される可能性があります。異なる乱数を生成させるには、srand関数で初期化する必要があります。目的に応じて初期化の方法を選ぶことが大切です。
目的にあわせて使い分けることができるように、この記事を何度も参考にして下さいね!