[追記]
この記事は2014年、私が文系大学生の頃、手探りでプログラミングを独学し始めた頃の記事です。温かい気持ちで見ていただけたら幸いです。
ーー
擬似乱数について初めて知ったのでメモしておきます(((o(゚▽゚)o)))
初学者丸出しでとても恥ずかしいのですが…。><
rand()について
まず、この「じゃんけんゲーム」のプログラムを写経していた時でした。
/* ***************************************/ /* ファイル名 janken.c じゃんけん。 */ /* プログラム概要 乱数と条件分岐を使用したゲーム */ /* ***************************************/ #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { /* 1/2/3 = グー/チョキ/パー */ int iPlayer; int iComputer; short sLoop_flg = 1; time_t tSeed; tSeed = time((time_t *) 0); //?? srand((unsigned int) tSeed); //?? while (sLoop_flg) { printf("*** 1グー、2チョキ、3パー、4終了 ***\n"); printf("1~4のうち1つを入力 → "); scanf("%d", &iPlayer); iComputer = rand() % 3 + 1; /* それぞれの場合で勝敗を出力 */ switch (iPlayer) { case 1: printf(" あなた: グー\tVS"); switch (iComputer) { case 1: // VS グーcom /* ...以下続く */
グー/チョキ/パーの入力をそれぞれ 1, 2, 3 とし、プレイヤーがその数字をiPlayerに入れて、
コンピュータの方は1,2,3それぞれランダムでiComputerに入れ、
iPlayerとiComputerそれぞれの場合で(switch文で) 勝敗を出力するやつです。
ここで 17-20行目の
> tSeed = time((time_t *) 0);
> srand((unsigned int) tSeed);
がよく分からなかったので、調べました。
このページを見ました。
『乱数を得る』(2014/2/8アクセス)
< http://wisdom.sakura.ne.jp/programming/c/c61.html >
私は “rand() は乱数を得る関数だ” という表面の認識しか無く、
「きっとやるたびにランダムな数字が得られるのだろう」
と思っていました。
なので、
#include <stdio.h> #include <stdlib.h> int main() { int count; for (count = 0 ; count < 10 ; count++) printf("%d\n" , rand()); return 0; }
これを実行した時に
16807
282475249
1622650073
984943658
1144108930
470211272
101027544
1457850878
1458777923
2007237709
% ./random
16807
282475249
1622650073
984943658
1144108930
470211272
101027544
1457850878
1458777923
2007237709
と、同じ結果が返ってきてとても驚きましたΣ(・□・;)
えええ! これじゃランダムじゃないじゃん!って!
それで、さっきのページに
そこで、擬似乱数を生成するための初期値を変更します。
初期値を指定しなかった場合は 1 を初期値として擬似乱数を出しています
初期値の指定はsrand()関数が行います
メモメモφ(゚▽゚)oメモメモ
なのでためしにsrand()を入れて初期値を1から変えるのを試してみました。さっきのやつに1行足します。初期値が1とのことなので、とりあえず2にしてみます。
#include <stdio.h> #include <stdlib.h> int main() { int count; srand(2); for (count = 0 ; count < 10 ; count++) printf("%d\n" , rand()); return 0; }
実行しました
33614
564950498
1097816499
1969887316
140734213
940422544
202055088
768218109
770072199
1866991771
% ./random
33614
564950498
1097816499
1969887316
140734213
940422544
202055088
768218109
770072199
1866991771
(((o(゚▽゚)o)))おおっ 変わった!
最初の数字が
16807
から
33614
と、2倍になっています。でも他もそうかというと、そうじゃないみたいです。
rand()が中でどんな計算で擬似乱数を作っているのか分からないけど、とりあえず
「rand()は初期値を変えないと同じ数字を返す」
ってことを新しく知りました!
なので、本来の意味での「ランダムな」数字を得るためには、このsrand()に与える数字(初期値)をつねに変えていくのが良さそうです(((o(゚▽゚)o)))
そのために便利なのが、時刻を利用することだったのか! と、最初写経してたじゃんけんコードを見て思いました。
ちなみに、この、擬似乱数生成の元となる値を「random seed (乱数種)」というらしいです! 毎回異なる数値が必要になる場合にはプログラムのはじめにシードの初期化が必要なようですね。
time()について
リファレンス見ます。
man 2 time
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/time.2.html
> time_t time(time_t *t);
> time() は、紀元 (Epoch; 1970-01-01 00:00:00 (UTC)) からの秒数で返す。
> もし t が NULL でなかったら返り値は t の指しているメモリにも格納される。
つまり、NULLポインタを渡すと、UNIX時間が得られるようです(((o(゚▽゚)o)))
実行した時間ごとに値が変わる性質は便利ですね!
ということで、最初に上げた「よく分からなかった」箇所
time_t tSeed; tSeed = time((time_t *) 0); srand((unsigned int) tSeed);
この2つ目のものについてです。
tSeed = time((time_t *) 0);
tSeed は time_t型の変数です。
これは、0をtime_t型のポインタへキャストし、NULLポインタにしているようです。(* 0は、ポインタにキャストしたらNULLポインタになる、と、規格で決まっているみたいです! *)
なので、time関数にNULLを渡しているので、この time_t型の変数 tSeed には現在のUNIX時刻が格納されます。
/* でも普通はこれは
tSeed = time(NULL);
* と書くようですね。(実際これで問題無く動きます。)*/
それで、20行目の
srand((unsigned int) tSeed);
に繋がるわけですね!(((o(゚▽゚)o)))
これで、乱数の初期値を現在時刻(UNIX時刻)にできたわけです!! 感動です!(((o(゚▽゚)o)))わーい!これで毎回違う乱数を得られるぞ!
でもめっちゃ速く(1秒未満の間隔で)実行したら同じ数が返ってきそうですね!
ちなみにこの辺とっても自信なかったのでTwitterで(深夜に)おそるおそる尋ねたりしてました
私ポインタとかポインタへのキャストとかよく分からないのですが、
time_t tSeed;
tSeed = time((time_t *) 0);
結局これは「0をtime_t型のポインタへキャストしてる(NULL)」でいいんですよね? ><(自信ない)
— ちょまど@お勉強中 (@chomado) 2014, 2月 8
こうして得られた初期値を基に、20行目の
iComputer = rand() % 3 + 1;
という操作により、iComputerに無事ランダムな1~3の整数が入るわけですね!(((o(゚▽゚)o)))やったあ!
分かってスッキリです!!
とりあえず、謎の3行が何をしているのかがわかったので、今日はここまでです!(((o(゚▽゚)o)))
[…] コメントをどうぞ 美少女のちょまどさんが何やら呟いていたので. http://chomado.ciao.jp/blog/programming/randomseed/初学者丸出しで恥ずかしいけど日記を書きました @chomado […]
[…] Random Seedって言葉を初めて知った! — Madoka Chomado […]