乱数の生成(rand, srand)

    2012/05/19

    本章では下記の内容を学習します。

    • 乱数の生成: rand 関数
    • 乱数の種を設定: srand 関数
    • 乱数の幅を決める: 剰余演算

    乱数の生成(rand 関数)

    #include <stdio.h>
    #include <stdlib.h> // rand関数
    
    int main( )
    {
      // 例1: 乱数の生成(rand関数)
      int     val;
    
      // rand関数は0~32767の整数を返す
      val =   rand();
    
      printf( "ランダムに決定された値は%dです\n", val );
    
      return  0;
    }
    

    変数の値を決める方法には、プログラムであらかじめ決めておくか、scanf_s 関数を利用してユーザに入力してもらう方法がありました。今回新たに学習するのは、プログラムがランダムに決めてくれるという方法です。

    rand 関数は、プログラム実行時にランダムな値を生成してくれる関数です。実行すると、プログラムはランダムな整数値を決定してくれます。

    rand 関数が決定できる乱数は、0 ~ 32767 までの整数値です。これと異なる範囲の数値が欲しい場合は、工夫してやる必要があります。この方法は後述します。

    rand 関数を利用するためには、<stdlib.h> というヘッダファイルを include する必要があります。お約束の #include <stdio.h> の下に付け加えてください。

    rand 関数は現状では、プログラムを実行するたびに、同じ値を返してしまいます(多くの環境では 41 が返ってくるでしょう)。これはランダム値の初期設定を行っていないためです。初期設定の方法は、次項で解説します。

    乱数の種を設定(srand 関数)

    srand 関数による乱数の初期化

    #include <stdio.h>
    #include <stdlib.h> // rand, srand関数
    #include <time.h>   // time関数
    
    int main( )
    {
      // 例2: 乱数の種を設定(srand関数)
      int     val2;
    
      // srand関数で、乱数パターンを初期化する
      // 乱数パターンの初期値は、現在時刻にしてやると、
      // プログラム実行ごとに異なるパターンが使える
      unsigned int    now = (unsigned int)time( 0 );
      printf( "乱数の種を現在時刻(%u)で初期化します\n", now );
      srand( now );
    
      // 毎回異なる乱数が取得できる
      val2    = rand();
    
      printf( "ランダムに決定された値は%dです\n", val2 );
    
      return  0;
    }
    

    rand 関数は、初期設定を行わない場合、常に同じランダムパターンを返すことになっています。これでは乱数の意味をなさないことになります。当たらない区間に入っている抽せん機に、積極的にお金をつぎ込んでくれる人は、そういないはずです。そこで、パターンを適切に初期化するための関数を利用します。

    srand 関数は、rand 関数が返す乱数の初期設定を行う関数です。srand 関数は引数として、初期設定パターンの値を要求してきますので、これに『プログラム実行ごとに、必ず変わる値』を与えてやれば、乱数パターンは毎回必ず変わるはずです。この値は『現在時刻』が最も適しているといえます。

    time 関数は、現在時刻を、1970 年 1 月 1 日からの経過秒数で返します。2012 年 5 月現在で、1337300000 くらいの値が返ってきます。この値は 1 秒ごとに刻々進んでいきますから、プログラム実行時に毎回変化することになります。少し考えを巡らせると、この値そのものを乱数として利用することもできるのではないかと思いつきますが、この値は『簡単に推測できる値』ですから、乱数としては正しくありません。

    time 関数を利用する場合は、<time.h> を include する必要があります。srand 関数とセットで利用することが多いですから、<stdlib.h> と一緒に include するように覚えておくとよいでしょう。

    rand 関数は、srand 関数で初期設定されると、以後はその設定を基準にした乱数を返すようになります。srand 関数を実行するごとに乱数パターンはリセットされてしまいますから、逆に 2 回以上実行する必要はありません。プログラムの最初で、一回だけ行えば OK です。

    srand 関数実行後、rand 関数を数回実行しておく

    // srand関数で、乱数パターンを初期化する
    unsigned int    now = (unsigned int)time( 0 );
    printf( "乱数の種を現在時刻(%u)で初期化します\n", now );
    srand( now );
    
    // 5回ほど実行してやると、乱数パターンが読めなくなる
    rand(); rand(); rand(); rand(); rand();
    

    例 2 のプログラムを複数回実行してくれた人は感づくと思いますが、srand 関数を実行した直後の、1 回目の rand 関数の値は、前回の実行と比べて 1 秒単位で 1 だけずれた値が返ってくると思います

    これは rand 関数のクセなのですが、rand 関数は初期設定後、数回実行しないと乱数としての本領が発揮されません。ですから、srand 関数を実行したら、rand 関数を 5 回ほど実行してあげてください。これでパターンが読めない乱数になると思います。

    乱数の幅を設定

    剰余演算法

    #include <stdio.h>
    #include <stdlib.h> // rand, srand関数
    #include <time.h>   // time関数
    
    int main( )
    {
      // 例4: 剰余演算法による乱数幅の決定
      srand( (unsigned int)time( 0 ) );
      rand(); rand(); rand(); rand(); rand();
    
      // 10で割った余りを取得すれば、0~9の値が得られる
      int     ran1    = rand() % 10;
      printf( "ランダムに決定された値は%dです\n", ran1 );
    
      return  0;
    }
    

    rand 関数で得られる乱数の幅は、0 ~ 32767 の範囲です。この幅ちょうどに乱数を利用するのであれば問題ありませんが、ふつうは 0 ~ 9 の範囲で欲しいだとか、100 ~ 999 が必要だとか、シチュエーションによって取得したい乱数は異なってくると思います。

    剰余演算を利用すると、0 ~ 32767 の範囲を、『0 ~好きな値』に変更できます。手法は単純で、乱数値を好きな値で割った余りを取ればよいのです。たとえば 10 で割った余りならば、必ず 0 ~ 9 の範囲に収まります。

    実践問題: 乱数幅の調整
    1. 0 ~ 999 までの乱数を取得できるようにしてください
    2. 100 ~ 999 までの乱数を取得できるようにしてください
    3. 0 ~ 1000 まで、100 刻みでの乱数を取得できるようにしてください