https://youtu.be/Be9UdOKmtdY
\textcolor{pink}{四国めたん: }教師役ですわ
\textcolor{lime}{ずんだもん: }生徒役なのだ
\footnotesize \textcolor{pink}{四国めたん:} 皆さん、こんにちは。四国めたんです
\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回もC言語のお勉強をしていきましょう
\footnotesize \textcolor{lime}{ずんだもん:} レッツゴーなのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回は、少し趣向をかえて、 乱数 の生成についてのお話しをしますわ
\footnotesize \textcolor{lime}{ずんだもん:} 乱数 はゲームを作成するのに、とても役立つのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、ゲーム以外にも、暗号やID生成など、様々な用途で 乱数 が利用されていますわ
\footnotesize \textcolor{lime}{ずんだもん:} ところで 乱数 の正確な定義が良く判らないのだ
\footnotesize \textcolor{pink}{四国めたん:} 乱数 の定義については、この解説の範囲を逸脱するので、知りたい方はネットなどで確認してくださいね
\footnotesize \textcolor{lime}{ずんだもん:} わかった、確認しておくのだ
\footnotesize \textcolor{pink}{四国めたん:} ところで、 乱数 の生成は、コンピューターと非常に相性が良くないことはご存知ですか?
\footnotesize \textcolor{lime}{ずんだもん:} えぇ~、そうなのか?知らなかったのだ
\footnotesize \textcolor{pink}{四国めたん:} コンピューターの特徴の一つとして、一連の処理を正確に、そして順序良く熟していくことが挙げられますわ
\footnotesize \textcolor{lime}{ずんだもん:} たしかに...
\footnotesize \textcolor{pink}{四国めたん:} そこに、例えば 乱数 のような不確定な要素は存在しませんわ
\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、ゲームなどの特殊な用途を除けば、毎回、結果が異なる、もしくは意図した動作をしないようなコンピュータープログラムは失敗作でしかありませんわ
\footnotesize \textcolor{lime}{ずんだもん:} いわれてみればそうなのだ
擬似乱数について
\footnotesize \textcolor{lime}{ずんだもん:} じゃあ、 乱数 の生成が不得意なコンピューターで、どのようにして 乱数 を生成するのだ?
\footnotesize \textcolor{pink}{四国めたん:} 実は、世の中には凄い方々がいて、規則に沿って数値を与えると、 乱数 らしき 答えを得ることができる、数学的な方法を考えてくれていますわ
\footnotesize \textcolor{lime}{ずんだもん:} お~、すごいのだ
\footnotesize \textcolor{pink}{四国めたん:} これらの方法で得られる 乱数 のことを 擬似乱数 と呼びますわ
\footnotesize \textcolor{lime}{ずんだもん:} 方法は1つだけなのか?
\footnotesize \textcolor{pink}{四国めたん:} 擬似乱数 の生成方法は、一般に利用されるような簡単なものから、暗号に使用されるような、 これホントの乱数なのでは? と疑いたくなるような方式まで存在しますわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} では、なぜ 擬似乱数 と呼ばれるかというと、人には判らないレベルではあっても、微妙な偏りがあったり、繰り返しがあったりするからですわ
\footnotesize \textcolor{lime}{ずんだもん:} 本当の 乱数 には、かなわないのだ
C標準関数の乱数を使いましょう
\footnotesize \textcolor{pink}{四国めたん:} まぁ、ゲーム等に使うような乱数であれば、C標準関数のrand
関数を使えば、実用上、問題ないと思いますわ
\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、形式はint rand()
ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 詳しく、教えてほしいのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、まず、使用するには"stdlib.h"ファイルをインクルードする必要がありますわ
\footnotesize \textcolor{lime}{ずんだもん:} ファイルの先頭に#include <stdlib.h>
を記述するのだ
\footnotesize \textcolor{pink}{四国めたん:} そして、引数は無く、戻り値は0以上の整数で、呼び出す毎に異なる値が返りますわ
\footnotesize \textcolor{lime}{ずんだもん:} たしかに乱数っぽいのだ
\footnotesize \textcolor{pink}{四国めたん:} なお、戻り値として返る整数値の最大は、RAND_MAX
として"stdlib.h"内で定義されていますわ
\footnotesize \textcolor{lime}{ずんだもん:} RAND_MAX
は、実際にはいくつなのだ?
\footnotesize \textcolor{pink}{四国めたん:} システムごとに異なりますが、Visual Studioの場合は0x7FFF、つまり、32,767ですわね
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} とりあえず実際のプログラムを見てみましょう
#include <stdio.h>
#include <stdlib.h>
void main() {
for (size_t i = 0; i < 5; i++) {
int num = rand();
printf("%d ", num);
}
printf("\n");
for (size_t i = 0; i < 5; i++) {
int num = rand();
int mod = num % 100;
printf("%d ", mod);
}
printf("\n");
for (size_t i = 0; i < 5; i++) {
double num = rand();
double norm = num / RAND_MAX;
printf("%lf ", norm);
}
printf("\n");
}
\footnotesize \textcolor{lime}{ずんだもん:} たしかにバラバラな数値が出力されているのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、最初のforループでは、取得した乱数をそのまま出力していますわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} 次のループでは、100で割った余り、つまり、0~99の範囲の整数の乱数として出力していますわ
\footnotesize \textcolor{lime}{ずんだもん:} お~、こうすれば、任意の整数の範囲の乱数を得られるのだ
\footnotesize \textcolor{pink}{四国めたん:} 負数も範囲に含める場合には、一定の数を差し引けばOKですわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} 最後のループは、取得した乱数を浮動小数点数に変換後、最大値で割って、0.0~1.0の範囲の浮動小数点数の乱数として出力していますわ
\footnotesize \textcolor{lime}{ずんだもん:} こうすれば、任意の範囲の浮動小数点数の乱数を得られるのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、範囲を拡げたければ、任意の数をかければ得られますわね
\footnotesize \textcolor{lime}{ずんだもん:} ところで、forループ内のインデックスが全て"i"になっているのだが...
\footnotesize \textcolor{pink}{四国めたん:} はい、forループで使用するインデックス"i"は、ループが異なれば、別個のインデックスとして扱われますわ
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
もう少しバラバラに...
\footnotesize \textcolor{pink}{四国めたん:} とりあえず、もう一度プログラムを実行してみましょう
\footnotesize \textcolor{lime}{ずんだもん:} 当然、同じ結果を得ることができたのだ
\footnotesize \textcolor{pink}{四国めたん:} 乱数を使っているのに?
\footnotesize \textcolor{lime}{ずんだもん:} お~、そういえば乱数を使っているので、違う結果が出て欲しかったのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、出力された数値が前回と一緒なので、乱数としては失敗ですわ
\footnotesize \textcolor{pink}{四国めたん:} なにしろ、1回、プログラムを実行すれば、次からは得られる数値が予測できてしまうからですわ
\footnotesize \textcolor{lime}{ずんだもん:} どうしてなのだ?
\footnotesize \textcolor{pink}{四国めたん:} これは、rand
関数が、内部では計算で乱数を取得しているからですわ
\footnotesize \textcolor{lime}{ずんだもん:} つまり、初期設定を変えなければ、同じ結果が出てしまうということか
\footnotesize \textcolor{pink}{四国めたん:} はい、計算を正確に行うコンピューターの良い面が、裏目に出てしまった結果ともいえますわね
初期値を変えましょう
\footnotesize \textcolor{lime}{ずんだもん:} どうにかならないのか?
\footnotesize \textcolor{pink}{四国めたん:} 実は、rand
関数には、初期設定を変える関数srand
がセットで提供されていますわ
\footnotesize \textcolor{lime}{ずんだもん:} どのような関数なのだ?
\footnotesize \textcolor{pink}{四国めたん:} 形式はvoid srand(unsigned int seed)
となりますわね
\footnotesize \textcolor{lime}{ずんだもん:} 詳しく教えてほしいのだ
\footnotesize \textcolor{pink}{四国めたん:} 引数の"seed"に初期値となる値をセットするだけですわ
\footnotesize \textcolor{lime}{ずんだもん:} さっそく、srand
を追加して確認してみるのだ
#include <stdio.h>
#include <stdlib.h>
void main() {
srand(100);
for (size_t i = 0; i < 5; i++) {
int num = rand();
printf("%d ", num);
}
printf("\n");
for (size_t i = 0; i < 5; i++) {
int num = rand();
int mod = num % 100;
printf("%d ", mod);
}
printf("\n");
for (size_t i = 0; i < 5; i++) {
double num = rand();
double norm = num / RAND_MAX;
printf("%lf ", norm);
}
printf("\n");
}
\footnotesize \textcolor{lime}{ずんだもん:} 前回とは異なる数値が出力されたのだ
\footnotesize \textcolor{pink}{四国めたん:} とはいえ、srand
の"seed"にセットする値が同じであれば、毎回、同じ数値が得られることは変わりありませんわね
\footnotesize \textcolor{lime}{ずんだもん:} ダメではないか!
時刻を初期値に
\footnotesize \textcolor{pink}{四国めたん:} この問題は、srand
の"seed"に乱数で得られる値をセットすることで解消しますわ
\footnotesize \textcolor{lime}{ずんだもん:} 乱数を生成するのに乱数を使うのか???
\footnotesize \textcolor{pink}{四国めたん:} よく使われるのは、現在の時刻をsrand
の"seed"にセットする方法ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 時刻だと乱数にならないのではないか?
\footnotesize \textcolor{pink}{四国めたん:} たしかにプログラム中で時刻を取得する場合、 最初 以外は定期的な取得となるので、乱数にはなりませんわね
\footnotesize \textcolor{lime}{ずんだもん:} 最初 以外は?
\footnotesize \textcolor{pink}{四国めたん:} はい、人がプログラムを開始する時刻は、意図しない限りはランダムになりますわ
\footnotesize \textcolor{pink}{四国めたん:} ですので、最初に取得する時刻は、おおむね乱数として扱えますわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} なお、現在の時刻を取得するには、C標準関数のtime
を使用しますわ
\footnotesize \textcolor{lime}{ずんだもん:} どのような関数なのだ
\footnotesize \textcolor{pink}{四国めたん:} 形式はtime_t time(time_t* timer)
ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 詳しく教えてほしいのだ
\footnotesize \textcolor{pink}{四国めたん:} まず、使用するには"time.h"ファイルをインクルードする必要がありますわ
\footnotesize \textcolor{lime}{ずんだもん:} ファイルの最初に#include <time.h>
と記述するのだ
\footnotesize \textcolor{pink}{四国めたん:} そして、"timer"に変数へのポインタをセットすると、その変数には戻り値と同じ値がセットされますわ
\footnotesize \textcolor{lime}{ずんだもん:} 同じ値が得られるのであれば、ムダではないのか?
\footnotesize \textcolor{pink}{四国めたん:} まぁ、通常はNULLをセットして問題ないと思いますわ
\footnotesize \textcolor{pink}{四国めたん:} そして、戻り値は現在時刻で、単位は"秒"ですわね
\footnotesize \textcolor{lime}{ずんだもん:} time_t
とはなんなのだ?
\footnotesize \textcolor{pink}{四国めたん:} time_t
は時刻を表す整数の型ですので、そのままsrand
関数の引数にセットしても問題ありませんわ
\footnotesize \textcolor{lime}{ずんだもん:} とりあえず実例をおねがいするのだ
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void main() {
srand(time(NULL));
for (size_t i = 0; i < 5; i++) {
int num = rand();
printf("%d ", num);
}
printf("\n");
for (size_t i = 0; i < 5; i++) {
int num = rand();
int mod = num % 100;
printf("%d ", mod);
}
printf("\n");
for (size_t i = 0; i < 5; i++) {
double num = rand();
double norm = num / RAND_MAX;
printf("%lf ", norm);
}
printf("\n");
}
\footnotesize \textcolor{lime}{ずんだもん:} 実行するたびに得られる数値が異なるのだ
timeについて
time
関数で得られる値は、1970年1月1日0時0分0秒(UTC)からの経過時間で、秒単位です。
戻り値の time_t型 で表せる時間は29億年分くらいあるようなので、最大1秒以内にtime
関数を再実行する以外では、同じ値を取得することはないでしょう。
まとめ
\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした
\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ
\footnotesize \textcolor{pink}{四国めたん:} 以上で 乱数 についての説明は終わりですわ
Discussion