Index

HOME > プログラムTOP > C++



C++の壺・乱数篇

 C++の基本テクニックのTipsです。プログラミングでは、特定の数値ではなく、適当な数値を得たいことがよくあります。この時得る数値が、乱数と呼ばれるものです。乱数篇では、乱数の意味や、rand関数を使った基本的な乱数の生成方法を扱います。
[index] 擬似乱数 ... 擬似乱数とは
乱数の生成 ... rand関数を用いた乱数の生成
乱数の範囲指定 ... 指定した範囲の乱数の取得

sasaraan programming

Exposition

●擬似乱数

 乱数とは、一般に、出現の予測ができない数値のこと です。さいころやルーレットなどは、次に出る数字が予測できませんので、これにあたります。一方、一見適当に現れる数値でも、一定の計算式で算出されたものを擬似乱数といいます。プログラムで生成するのはこの擬似乱数です。
 通常、擬似乱数は、計算式(関数)と初期値が分かれば予測可能であり、真の乱数ではありませんが、多くのプログラムで実用上問題なく使われています。
 主な擬似乱数の生成方法には以下のようなものがあります。

  ・平均採中法
  ・線形合同法
  ・混合合同法
  ・メルセンヌ・ツイスター法

 どのような方法で擬似乱数を生成しているかは、各システムにより異なります。どの擬似乱数でも、通常は、初期値が同じであれば、常に同じ順番で同じ数字を生成します。C/C++ では、srand 関数と rand 関数を使って簡単に擬似乱数を生成できます。srand関数で初期値を設定し、rand関数で乱数値を取得して使用します。

●乱数の生成

 C/C++で乱数を生成するには、まず、srand関数で初期値 ( seed ) を設定します。初期値は同じ値であれば常に同じ乱数を発生させてしまいますので、初期値には、常に値が変わる現在の時間を代入することが多いようです。通常、これには、1970年からの経過時間を表す time関数を用います。
time 関数初期値の取得
srand 関数初期値の設定
rand 関数乱数値の取得
 各関数は次のような仕様になっています。
time function
・書式time_t time( time_t *sec );
・機能現在の経過時間を取得する
・引数sec...戻り値の格納場所
・戻り値1970年1月1日0時0分0秒 からの経過秒数
・要件time.h のインクルード
 ここで使われている time_t型は long型もしくは、これに準ずる整数型として定義されているはずです。srand関数で使用する際は、unsigned型に型変換する必要があります。戻り値は、引数で指定した位置に格納されますが、格納の必要がなければ NULL(指定なし)でもかまいません。
srand function
・書式void srand( unsigned int seed );
・機能乱数の初期値を設定する
・引数seed...乱数生成のための初期値
・戻り値なし
・要件stdlib.h のインクルード
rand function
・書式int rand( void );
・機能乱数を取得する
・引数なし
・戻り値擬似乱数 (0〜32767 の範囲の整数)
・要件stdlib.h のインクルード
 rand関数は連続して使用することにより、異なる乱数値を生成し続けます。ただし、一通り、乱数値を生成し終わると、通常は、また同じ順番で同じ数値を生成します。
#include <stdio.h>     // printf使用
#include <stdlib.h>    // rand, srand使用
#include <time.h>     // time使用
#include <conio.h>    // getch使用

// 乱数の生成テスト
void main() 
{
    int i;

    // 乱数の seed 値のセット
    srand((unsigned) time(NULL));

    // 100個の乱数を生成して出力
    for (i=0; i<100; i++) { 
        int n = rand();
        printf("%d\n", n);
    }

    if (gecth()) return;
}	
// 出力例
3605
29730
22542
4596
3533
30418
18074
3262
11697
29371
21081
23419
28110
17804
7052
10338
17369
7696
24995
...

●範囲の指定

 rand関数では特定の範囲の値 (0...32767) しか取得できません。範囲を絞り込んで取得するには、%演算子を用いた剰余計算を利用するケースが多いようです。具体的には以下のようになります。

求める乱数 = rand() % 範囲の要素数 + 範囲の最小値

 例えば、ある整数を 5 で割った余りは、必ず 0〜4 の間の整数となりますので、この値を用います。また、最小の数が 0 以外(例えば 範囲 : 5〜9 )の時は、出た剰余の値に最小値 ( 5 ) を加算してやれば求める乱数値を取得できます。
#include <stdio.h>     // printf使用
#include <stdlib.h>    // rand, srand使用
#include <time.h>     // time使用
#include <conio.h>    // getch使用

// "0 〜 x" の範囲の乱数の生成
void main() 
{
    // 乱数の初期値のセット
    srand((unsigned) time(NULL));

    // 0〜9 までの乱数を 100回生成
    int i, n;
    for (i=0; i<100; i++) { 
        n = rand() % 10;  // 10は0〜9の要素数
        printf("%d, ", n);
        if (i % 5 == 4) printf("\n"); // 一行に5個
    }

    if (gecth()) return;
}	
// 出力例
3, 7, 3, 0, 8,
8, 3, 8, 9, 5,
8, 4, 6, 2 ,4,
9, 8, 7, 4, 6,
9, 2, 0, 7, 4,
7, 1, 4, 6, 0,
5, 0, 0, 9, 1,
5, 1, 3, 4, 3,
6, 5, 6, 7, 5,
4, 5, 3, 9, 5,
4, 1, 1, 7, 2,
4, 6, 8, 7, 5,
4, 9, 2, 0, 7,
1, 5, 1, 5, 8,
8, 1, 9, 7, 1,
5, 4, 8, 6, 9,
1, 1, 1, 4, 8,
1, 1, 7, 4, 5,
0, 3, 8, 7, 9,
4, 9, 6, 0, 5,
#include <stdio.h>     // printf使用
#include <stdlib.h>    // rand, srand使用
#include <time.h>     // time使用
#include <conio.h>    // getch使用

// "x 〜 y" の範囲の乱数の生成
void main() 
{
    // 乱数の初期値のセット
    srand((unsigned) time(NULL));

    // 97〜122 までの乱数を 100回生成
    int i, n;
    for (i=0; i<100; i++) { 
        n = rand() % 26 + 97; // 要素数26
        printf("%c, ", n);        // 文字として出力
        if (i % 5 == 4) printf("\n"); // 一行に5個
    }

    if (gecth()) return;
}	
// 出力例
b, g, j, i, p,
o, f, b, n, r,
r, p, y, f, k,
u, c, f, v, a,
n, c, h, t, d,
p, p, t, h, s,
o, o, e, t, i,
c, i, d, n, g,
j, k, k, z, v,
r, a, i, o, v,
u, b, e, e, h,
p, n, z, h, z,
w, c, i, i, t,
h, x, i, e, s,
i, k, l, q, b,
w, d, k, q, y,
i, d, z, b, l,
i, l, i, n, h,
j, x, b, r, h,
g, q, m, l, b,

www.sasaraan.net

(c) morijoh