lC言語 サンプル (乱数) '97. 4.29  
l.お知らせ 内容 LINK FILE   HTML Win PC Unix MS-DOS C C++ Mfc Java 
.C言語_ 3 フォ-マットコ-ド 6 演算子 9 FOR  集合デ-タ  目的別
1 表示してみる 4 変数と定数 7 条件判断 10 WHIILE  ポインタ     
2 エスケ-プ コ-ド 5 デ-タ入力 8 分岐   未使用  関数   ★ 
 用法  文章のコピー  参考ページ 
 (サンプル)...   乱数  ダンプ  その他( 基本  応用 ) 
乱数の範囲を指定する方法について
数の桁数をふやすには
10進数の1桁は 0〜9 です。
0〜9 を n で表わし、n を使って2桁の10進数 m を表わすには、次のようにします。
  m = n×10 + n
 
   n×10 は 0, 10, 20, 30 . . . 90 の十の位を表し、
n で 0〜9 の 1の位を表わします。

同じようにして、桁数を増やして行くことができます。
  m = n×10 + n
  m = n×10×10 + m
  m = n×10×10×10 + m
  m = n×10×10×10×10 + m
   10 の位と、
100 の位と、
1000 の位と、
10000 の位と、
1 の位。
10 以下の位。
100 以下の位。
1000 以下の位。

乱数の桁数を増やすには
Cコンパイラの関数 rand() は乱数を作りますが、その範囲は 0〜32767 です。
これを 32768 進数の1桁と考えて、10進数の場合と同様に桁数を増やすことができます。
(人間の指は10本ですが、rand() には指が 32768 本あるのかもしれません。)
0〜32767 を n で表わし、n を使って大きな数 m を表わすには、次のようにします。
m = n
m = n×32768 + n
m = n×32768×32768 + n×32768 + n
  0〜32767
0〜1073741823
0〜3.52×10の13乗
  32768 進数の1桁
32768 進数の2桁
32768 進数の3桁

乱数の上限を決める(1)
0〜N の数を、これと比例する 0〜M の数に変換するのは、比例の計算です。
0〜N の乱数 n を、これと比例する 0〜M の乱数 m に変換する計算は、次のようになります。
  m=M× n
N
   n=0〜N
m=0〜M
ただし、元になる N の有効桁数は、M よりも十分大きくしておきます。
N の有効桁数が、M に必要な桁数よりも十分大きくなければ、m の値がとびとびになったりするなど、誤差が生じます。

上式によって得られる乱数 m は実用的ではありません。
M-1 や M が発生する確率は 1/N ですが、(M-1)〜M の間の値も、同じ確率で発生するからです。
(M-1)〜M の確率は、M 1個の確率よりも高くなってしまいます。

乱数の上限を決める(2)
0〜(N-1) の乱数 n を、これと比例する 0〜(M に近い数)の乱数 m に変換する計算は、次のようになります。
  m=M× n
N
   n=0〜(N-1)
m=0〜(M-1/N)
N が十分大きければ m は M に近い数になりますが、M 未満です。

Cコンパイラの関数 rand() を使って 0〜(M+1)未満 の範囲の乱数を得るには、次のようななプログラムを作ります。
得られた乱数の小数部分を捨てれば、0〜M の整数の乱数を得ることができます。
必要なときに呼び出して使えるように、関数にしています。
#include <stdlib.h> /* rand() */
double get_rand(double M)
{ /* double M;         M には必要な乱数の上限値を代入しておきます。 */
  double m;         /* m には計算の結果として、0〜M の値が代入されます。 */
  double n;      /* Cコンパイラの関数 rand() の乱数の値を代入するのに使います */
  double N=32768.;  /* rand() の最大値+1 の値 */
  n = (double)rand();           /* 0〜32767 の乱数 */
  n = N*(double)rand()+n;       /* 0〜1073741823 の乱数 */
  n = N*N*(double)rand()+n;     /* 0〜3.52×10の13乗 の乱数 */
  n = N*N*N*(double)rand()+n;   /* 0〜1.15×10の18乗 の乱数 */
  n = N*N*N*N*(double)rand()+n; /* 0〜3.78×10の22乗 の乱数 */
  m = (M+1)*n/N/N/N/N/N;        /* 0〜M の乱数が得られます */
  return m;
}
メモ : rand() が作る乱数は int 型なので、(double)により double型にキャストしています。
 int 型では、-32768〜32767 の範囲の整数値しか扱うことができません。
 上例は、計算式を整理すると無駄な計算を除くことができます。

 上記の乱数 m は、x=(int)m; のように int 型にキャストすれば、小数部分が捨てられて整数値になります。

32767 よりも十分小さい乱数だけが必要な場合は、int 型のままでも計算できます。
   m = (M+1)*(0〜32767)/32768; 

乱数の下限を決める
m1〜m2 の範囲の乱数が必要な場合は、
1. m1 と m2 の差、M=m2-m1 を求め、
2. 0〜M の範囲の乱数 m を作り、
3. m に m1 を加えます。
   m = (M+1)*(0〜32767)/32768;

m1〜m2 の範囲の乱数を作る関数の例を示します。
m2 と m1 の差 M を求め、上記の関数 get_rand() を呼び出して、0〜M の乱数を作り、 最後に m1 を加えて m1〜m2 を得ます。
#include <stdlib.h> /* rand() */
double get_rand2(double m1, double m2)   /* m1=最小値, m2=最大値 */
{ double m;       /* この変数に色々な値を代入します */
  m=m2-m1;        /* m2 と m1 の差を求めます */
  m=get_rand(m);  /* 上記の get_rand() を呼び出して、0〜m の乱数を得る */
  m=m+m1;         /* 0〜m に m1 を加えて、m1〜m2 の乱数にする */
  return m;
}

int 型の値が必要な場合は、次のように変更します。
get_rand() を呼び出す場合に、引数を double 型に変換して与えます。
#include <stdlib.h> /* rand() */
int get_rand3(int m1, int m2)
{ int m;
  m=m2-m1;
  m=get_rand((double)m); /* m を double型に変換して get_rand() に与えます */
  m=m+m1;
  return m;
}

乱数の発生頻度に勾配を付ける
Cコンパイラの関数 rand() が作る乱数は 0〜32767 の範囲の数が一様な確率で出現します。
つまり、0 も 100 も同じ確率で発生します。

平方根 sqrt() を2重に使用して、1 の発生確率を高くする例を示します。
1. まず十分大きな範囲の乱数を作ります。
2. 数学関数で変換します。これで、得られる数の勾配が変わります。
  例 sqrt(sqrt(n))  ...変換後の乱数。n は元の乱数
3. 変換後に得られる最大値で割って、0〜1 の乱数に変換します。
  例 sqrt(sqrt(n))/sqrt(sqrt(m));  sqrt(sqrt(m)) は変換後に得られる最大値
4. 必要な最大値を掛けて、必要な範囲の乱数にします。
5. 目的に応じて、勾配を逆にします。
#include <stdlib.h> /* rand() */
#include <math.h> /* sqrt() */
int get_randF(int N)
{ double m=1000000000;             /* 100000000 元になる乱数の大きさ */
  double n=get_rand((double)m);    /* 0〜100000000 の乱数 */
  n=N*sqrt(sqrt(n))/sqrt(sqrt(m)); /* 0〜N の乱数 (N の頻度が最高) */
  return N-(int)n;                 /* 1〜N (確率の勾配を逆にする) */
}
発生実例 (N=10 の場合に、1〜10 が発生した回数)
0=0 1=3390 2=2423 3=1731 4=1138 5=667 6=386 7=191 8=62 9=12 10=0 5=653 6=407 7=165 8=67 9=15 10=1 11=0


プログラム例
long double型の 1000〜1010 の乱数を発生して、long double型のまま表示し、 次に int型に変換して表示します。

有効桁数が少なくてもよい場合は、double型 または int型にします。
long double型は型名が長いので、data という別名を定義して使っています。

set_rand()は srand()を呼び出して、rand()が発生する乱数の種をセットします。
get_rand()は rand()を呼び出して、0〜(N+1)未満の乱数を発生します。
get_rand2()は m1〜(m2+1)未満 の乱数を発生します。get_rand()を使います。
#include <stdio.h>    /* printf を使うために必要 */
#include <stdlib.h>  /* srand rand sqrt         */
#include <time.h>    /* time clock              */
#define data long double /* long double 型の別名を、data と定義する */

/*◆ 乱数ジェネレータ rand() を初期化する */
void set_rand()
{ srand( (unsigned)(time( NULL ) ) ); }

/*◆ 0〜(N+1)未満の乱数を発生する */
data get_rand(data N)
{ data i,n,c1,c2;
  N=N+1;      /* 0〜(N+1)未満の乱数を発生するために、N を +1 する */
  c2=32768L; c1=32767L; /* 32767 は rand() が発生する乱数の最大値 */
  n=N*(data)rand()/c2;          /* 大きな桁数の乱数の最上位を作る */
  n=n+N*(data)rand()/c2/c2;       /* 以下、下の桁も乱数にして行く */
  n=n+N*(data)rand()/c2/c2/c2;
  n=n+N*(data)rand()/c2/c2/c2/c2;
  n=n+N*(data)rand()/c2/c2/c2/c2/c2;
  return n;                       /* 0〜(N+1)未満の乱数を返す */
}

/*◆ m1〜(m2+1)未満の範囲の乱数を発生する */
data get_rand2(data m1, data m2)   /* m1=最小値, m2=最大値 */
{ data m;         /* この変数に色々な値を代入します */
  m=m2-m1;        /* m2 と m1 の差を求めます */
  m=get_rand(m);  /* 上記の get_rand() を呼び出して、0〜m の乱数を得る */
  m=m+m1;         /* 0〜m に m1 を加えて、m1〜m2 の乱数にする */
  return m;
}

main() /***************************************************/
{ double n; int i,j=0,N;
  set_rand();                 /* rand()を初期化します         */

  for(i=0;i<30;i++)           /* 30 回のループ */
  { n=get_rand2(1000.,1010.); /* 1000〜1011未満 の乱数を発生  */
    printf("%.12f  ",n);      /* 小数点以下 12 桁で、小数表示 */
    j++;
    if(j>3){ j=0; printf("\n"); } /* 4 個表示したら改行       */
  }
  printf("\n\n");                 /* 全部表示したら改行       */

  for(i=0;i<100;i++)          /* 100 回のループ               */
  { n=get_rand2(1000.,1010.); /* 1000〜1011未満 の乱数を発生  */
    N=(int)n;                 /* int 型に変換 (小数点以下が捨てられる。) */
    printf("%d ",N);          /* 表示 */
  }
  printf("\n");               /* 改行 */
}


T

  mtoga@sannet.ne.jp   登録日 '96. 6.15
URL : http://www.page.sannet.ne.jp/mtoga/index.html