【C言語】
rand関数の使い方
-乱数の結果が毎回同じになる時の対処方法-

■この記事の概要

この記事では、C言語の乱数関数rand()が同じ結果を返す問題を解決する方法を解説します。

rand()を正しく使うためには、srand()関数を使用して乱数の種(シード)を設定する必要があります。

例として、srand(time(NULL))を使ったコードも紹介し、毎回異なる結果を得る方法を詳述しています。

実務ではより安全なrandom()関数の使用も推奨されています。

■乱数の種まきをする

●srand関数による乱数の種まきをしないと
結果がいつも同じになります。
●実務ではrand関数よりrandom関数を使おう。


■乱数の種をまかないと結果はいつも同じ

#include    <stdio.h>
#include    <stdlib.h>
#include    <time.h>
void 乱数の種なし(void){    
    printf("%s\t",__func__);
    
    for(int i = 0 ;i<10;i++){
        switch (rand() % 4)		{
        case 0: printf("風");   break;			    
        case 1: printf("林");   break;
        case 2: printf("火");   break;
        case 3: printf("山");   break;
        }
    }
    printf("\n");
}
int main(void){
    乱数の種なし();
}

乱数の種を蒔いていないので、何度実行しても同じ結果になります。

●乱数の種を蒔かないと毎回同じ実行結果

+ ./a.out
乱数の種なし    山火林山林山火風林林
+ sleep 1
+ ./a.out
乱数の種なし    山火林山林山火風林林
+ sleep 1
+ ./a.out
乱数の種なし    山火林山林山火風林林
+ sleep 1
+ ./a.out
乱数の種なし    山火林山林山火風林林
+ sleep 1

■乱数の種まきを追加した例

#include    <stdio.h>
#include    <stdlib.h>
#include    <time.h>
void 乱数の種あり(void){    
    printf("%s\t",__func__);
    
    srand((unsigned int) time(NULL));//★乱数の種まき=seed設定
    for(int i = 0 ;i<10;i++){
        switch (rand() % 4)		{
        case 0: printf("風");   break;			    
        case 1: printf("林");   break;
        case 2: printf("火");   break;
        case 3: printf("山");   break;
        }
    }
    printf("\n");
}
int main(void){
    乱数の種あり();
}

srand関数を使用して乱数の種を蒔きます。
乱数の種として現在時刻time(NULL)を使用するのが一般的です。

●毎回違う実行結果

+ ./a.out
乱数の種あり    風火風山林山火山山山
+ sleep 1
+ ./a.out
乱数の種あり    山火風火火風山火林火
+ sleep 1
+ ./a.out
乱数の種あり    火山火林風山火風林火
+ sleep 1
+ ./a.out
乱数の種あり    風山山火林林風風火林
+ sleep 1

注意:1秒以内に ./a.out を連打すると、
time(NULL)の結果が変わらないので乱数の結果も変わりません。


■じゃんけんプログラムの例

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef enum {
    グー,チョキ,パー,    
} じゃんけん;
int main(void) {
    srand((unsigned int)time(NULL));
    for(int i = 1; i <= 10; i++) {
        printf("%d回目: ", i);
        じゃんけん  私      = rand() % 3;
        じゃんけん  貴方    = rand() % 3;
        switch(私){
        case グー:
            switch(貴方){
            case グー:     puts("引き分けGG");  break;
            case チョキ:   puts("私の勝ちGC");  break;
            case パー:     puts("私の負けGP");  break;
            }
            break; 
        case チョキ: 
            switch(貴方){
            case グー:     puts("私の負けCG");  break;
            case チョキ:   puts("引き分けCC");  break;
            case パー:     puts("私の勝ちCP");  break;
            }
            break;     
        case パー:  
            switch(貴方){
            case グー:     puts("私の勝ちPG");  break;
            case チョキ:   puts("私の負けPC");  break;
            case パー:     puts("引き分けPP");  break;
            }
            break;   
        }
    }
}

ここまで読んでいただいて恐縮ですが乱数を生成には
rand関数ではなくて
random関数のほうが良いそうです。
詳細は下記CERT-Cを参照ください。

参考:CERT-C MSC30-C. 疑似乱数の生成に rand() 関数を使用しない