Part.12

乱数

内容

  • 乱数 (rand関数とsrand関数)

乱数

さまざまな場面で乱数を使う。ここでは、C言語に標準的についている疑似乱数関数を紹介する。本来はより性質の良い疑似乱数生成関数を使う方が良いだろうが、それはより高度なことをする事になったときで良いだろう。

rand() 関数

rand() 関数は、その関数が呼ばれるたびに、疑似乱数値として整数値を返します。例えば、次のプログラムでは、10個の疑似乱数値を画面に表示します。次のプログラムをファイル名 Part12-1.c としてエディターで打ち込み、 cc コマンドでコンパイルし、実行してみてください。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i, r;

    for(i = 0; i < 10; i++)
    {
        r = rand();
        printf("%d\n", r);
    }
    return 0;
}

注意1: rand() 関数を使う場合、 #include <stdlib.h> が必要となる。(過去の復習:数学関数を用いるときには #include <math.h> 必要であった。)

注意2:ここのサンプルプログラムにおいては、main関数の最後に return 0; を書いている。これは、main関数の戻り値として int が設定してあり、それに対応するものだが、当面あまり気にしなくて良い。

srand() 関数

rand() 関数は、ある初期値、ある規則に従って求まる数列なのであるが、なにもしないといつも初期値が同じであるため、同じ数列が得られるが, srand() 関数を用いることで、数列の初期値(正確には、疑似乱数列をつくりだす、漸化式の初期値)を変更できる。利用方法は、

srand(100);

等と、プログラムのはじめの方で一度呼び出すだけでよい。今は 100 としたが、例えば、皆さんの学籍番号等とすれば各人異なる結果が得られる。ただし、srand() 関数の引数は整数値である。

srand() 関数は例えば次のように使う。上記サンプルプログラムに srand() 関数を埋め込んだ。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i, r;

   srand(100);

    for(i = 0; i < 10; i++)
    {
        r = rand();
        printf("%d\n", r);
    }
    return 0;
}

このように、main() 関数のはじめの方で一度だけ呼び出す。関数を用いた例では次のような使い方が正しい。

#include <stdio.h>
#include <stdlib.h>
void foo();

int main()
{
   srand(100);
   foo();
   foo();
   return 0;
}

void foo()
{
    int i, r;

    for(i = 0; i < 10; i++)
    {
        r = rand();
        printf("%d\n", r);
    }
}

例えば、次の2つの例は間違いである。実行すると同じ数値が10個表示される。(漸化式の初期値をいつも100とし直しているので、当然である。)

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i, r;

    for(i = 0; i < 10; i++)
    {
        srand(100);
        r = rand();
        printf("%d\n", r);
    }
    return 0;
}

関数を使った間違った例

#include <stdio.h>
#include <stdlib.h>
void foo();

int main()
{

   foo();
   foo();
   return 0;
}

void foo()
{
    int i, r;

    srand(100);

    for(i = 0; i < 10; i++)
    {
        r = rand();
        printf("%d\n", r);
    }
}

サンプルプログラム

乱数を多く使う場合や [0,1) 一様乱数を用いたい場合には、次の様に #define 文を利用すると便利である。

#include <stdio.h>
#include <stdlib.h>
#define  RN   (rand()/(double)RAND_MAX)

int main()
{
    int i;

   srand(100);

    for(i = 0; i < 10; i++)
    {
        printf("%lf\n", RN);
    }
    return 0;
}

課題1

Part12-1.c を参考に、10回サイコロを振るシミュレーションを行うプログラムPart12-2.c を作成せよ(つまり、画面上に1~6の数値がランダムに10個出力されるプログラム)。

ヒント: % 演算子を使う。r が整数型(int)であるとき r % 6 とは整数 r を 6 で割った剰余(つまり 0~5)となる。

課題2

サイコロを10000回振るシミュレーションを行い、3の目が出る確率を計算し、画面に表示するプログラムを作成せよ。

課題3

サイコロを10000回振るシミュレーションを行い、1~6の目それぞれが出る確率を計算し、画面に表示するプログラム Part12-3.c を作成せよ。

ヒント: 各目の出た回数を記録する整数型の配列を用意すると良い。例えば、

int count[6];

と整数型の配列を用意し、それらを初めに全て 0 に初期化したのち、出たサイコロの目を r として、

count[r-1]++;

とすれば良いだろう。r-1 となっているのは、C言語の配列の添え字が0からはじまることによる。

課題4

呼ばれる度にランダムに 1~6 の数値を返す関数 dice() を作成せよ。また、先の課題3のプログラムをこの dice() 関数を用いるものに変更せよ。

課題5

一辺の長さが1の正方形領域内に上記の乱数を使ってでたらめに点を打つ。そのとき、半径1の円の中心が、正方領域のどこかの頂点にあるとして、でたらめに打った点がその円の中に入っている確率は π/4 であるから、近似的に円周率を求めることができる。(モンテカルロ法と呼ばれる。)

モンテカルロ法を用いて円周率の近似値を求めるプログラムを作成しなさい。