Random Seed って言葉を初めて知った!


!! おことわり !!
このブログには、いわゆる「技術記事」は一切ありません!!!
(書きたくても書けない)
ただの「勉強記録ノート」です!!!
プログラミング初学者の勉強記録ノートです!「日記」です!!

生暖かい目で見ていただけたらさいわいですヽ(;▽;)ノ

ーーーー

擬似乱数について初めて知ったのでメモしておきます(((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行目の

> time_t tSeed;
> 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;
}

これを実行した時に

% ./random
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;
}

実行しました

% ./random
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で(深夜に)おそるおそる尋ねたりしてました

こうして得られた初期値を基に、20行目の
iComputer = rand() % 3 + 1;
という操作により、iComputerに無事ランダムな1~3の整数が入るわけですね!(((o(*゚▽゚*)o)))やったあ!

分かってスッキリです!!

とりあえず、謎の3行が何をしているのかがわかったので、今日はここまでです!(((o(*゚▽゚*)o)))