トップ > サンプル解説 > 課題1

課題1: sample1.cpp — 確率乱数の発生

1週目。4 分布(一様・指数・正規・対数正規)の疑似乱数生成器を実装し、生成結果の度数分布が理論の確率密度関数と一致することを確認する。

目的

コンピュータ上で「ランダムさ」を再現するには疑似乱数を用いる。本課題では、標準一様乱数 \(U \sim U(0,1)\) を起点として、用途に合う 4 つの確率分布に従う乱数を生成する。

ソースコード(配布版)

下のテキストエリアには 配布/sample1.cpp の中身がそのまま貼り付けてある。// *** の箇所を埋めて、 「sample1.cpp をダウンロード」で自分の環境に保存できる。

実験手順

  1. 準備 上のテキストエリア(または 配布/sample1.cpp)の // *** を埋める:
    • 一様 → 区間変換: \(\mathrm{rnum} = (\beta - \alpha)\cdot u + \alpha\)
    • exp_dist逆関数法: \(y = -\dfrac{1}{\lambda}\ln(1-u)\)
    • normal_distBox–Muller 法: \(\theta = 2\pi u_2,\; z = \sqrt{-2\ln(1-u_1)}\cos\theta\)
    • lognormal_distパラメタ変換: \(v = \ln(1+\mathrm{var}_0/m_0^2),\; m = \ln m_0 - v/2\)
  2. 実行 DIST_TYPE を 1〜4 に変えて 4 回コンパイル・実行:
    g++ sample1.cpp && ./a.out > data.txt
    末尾の Average: ..., Variance: ... 行はツールが自動スキップするのでそのまま使える。
  3. 確認 専用ツール (histogram_check.html)data.txt を読み込み、度数分布表と理論 PDF を比較する。
    学習ポイント: 「確率密度 = 相対度数 / Δx」の Δx を自分で設定 する欄がある。ビン幅と同じ値を入れないと理論 PDF と縦軸が合わない — そのズレに気づくことで「Δx はビン幅」と体感できる設計。

出力形式と HTML との連携

メインループでは 1 行 1 サンプルを標準出力に、末尾で平均・分散のまとめ行を出力する。

0.289067
0.195331
0.741802
...
Average: 0.500151, Variance: 0.083120

Average: 行は HTML ツールが自動で除外するので、そのまま渡せる。

g++ sample1.cpp
./a.out > data.txt
可視化: histogram_check.html を開いて data.txt を指定、分布とパラメタ(sample1.cpp での設定と同じ値)を入れて「集計」を押す。 度数分布表・ヒストグラム棒・理論 PDF 曲線が重なって表示される。

主要な定数

DIST_TYPE1:一様, 2:指数, 3:正規, 4:対数正規。4 つとも動作させる必要あり。
NUM_TRIAL生成するサンプル数(既定 100,000)。大きいほど度数分布が滑らかに。
seed線形合同法の初期値 \(x_0\)。再現性のため固定(例: 19)。
PIBox–Muller で使用する \(\pi\)。

uniform_dist — 線形合同法

漸化式 \( x_{n+1} = (a \cdot x_n + c) \bmod M \) で整数列を作り、\(M{-}1\) で割って \([0,1)\) の値に変換する。

double uniform_dist(long *x){
  long M=65536, a=997, c=1;
  *x = (a*(*x)+c) % M;
  return (double)*x/(M-1);
}

学術的な品質は高くない(周期 \(\le 65536\))が、仕組みの学習用。長時間シミュレーションでは sample2/3 の ran2()(周期 \(\approx 2\times 10^{18}\))を使う。

exp_dist — 逆関数法 埋める

指数分布の累積分布関数 \( F(y) = 1 - e^{-\lambda y} \) を \(y\) について解いて \( y = F^{-1}(u) \) を得る:

\[ y = -\frac{1}{\lambda} \ln(1 - u), \quad u \in [0, 1) \]

u==1.0 の場合は \(\log(0)\) が発散するので do-while で排除している。

double exp_dist(long *x, double lam){
  double u;
  do { u = uniform_dist(x); } while (u == 1.0);

  double y = 0;
  // *** y の右辺を指数乱数が発生するように変形
  return y;
}

normal_dist — Box–Muller 法 埋める

2 つの独立な一様乱数 \(u_1, u_2 \in [0,1)\) から標準正規乱数 \(z \sim \mathcal{N}(0,1)\) を次で得る:

\[ \theta = 2\pi\, u_2, \qquad z = \sqrt{-2 \ln(1-u_1)} \cos\theta \]

平均 \(m\), 分散 \(\mathrm{var}\) の正規乱数は \( \sqrt{\mathrm{var}}\, z + m \)。return sqrt(var)*z+m の部分は埋まっている。

double normal_dist(long *x, double m, double var){
  double z, u1, u2, theta;
  do { u1 = uniform_dist(x); } while (u1 == 1.0);
  do { u2 = uniform_dist(x); } while (u2 == 1.0);

  theta = 0;
  // *** theta の右辺を変形
  z = 0;
  // *** z の右辺を変形
  return sqrt(var)*z + m;
}

lognormal_dist — パラメタ変換 埋める

平均 \(m_0\), 分散 \(\mathrm{var}_0\) の対数正規分布を得るには、内部の正規分布のパラメタを次で設定する:

\[ \mathrm{var} = \ln\!\left(1 + \tfrac{\mathrm{var}_0}{m_0^2}\right), \qquad m = \ln m_0 - \tfrac{\mathrm{var}}{2} \]

その正規乱数 \(z\) を exp(z) すれば、目的の対数正規乱数が得られる。

double lognormal_dist(long *x, double m0, double var0){
  double m = 0, var = 0;
  //*** 対数正規分布の平均 m0, 分散 var0 となるように
  //*** 正規分布の平均 m, 分散 var を設定
  double z = normal_dist(x, m, var);
  return exp(z);
}

各分布の理論 PDF(まとめ)

分布パラメタ確率密度関数 \(f(x)\)平均分散
一様\(\alpha, \beta\) \( \dfrac{1}{\beta-\alpha} \quad (\alpha \le x \lt \beta) \) \( \tfrac{\alpha+\beta}{2} \)\( \tfrac{(\beta-\alpha)^2}{12} \)
指数\(\lambda\) \( \lambda e^{-\lambda x} \) \( 1/\lambda \)\( 1/\lambda^2 \)
正規\(m, \mathrm{var}\) \( \tfrac{1}{\sqrt{2\pi\mathrm{var}}} \exp\!\big({-}\tfrac{(x-m)^2}{2\,\mathrm{var}}\big) \) \( m \)\( \mathrm{var} \)
対数正規\(m_0, \mathrm{var}_0\) \( \tfrac{1}{x\sqrt{2\pi v}} \exp\!\big({-}\tfrac{(\ln x - \mu)^2}{2v}\big) \)
\(v = \ln(1{+}\mathrm{var}_0/m_0^2),\ \mu = \ln m_0 - v/2\)
\( m_0 \)\( \mathrm{var}_0 \)

埋める箇所のまとめ ***