for WPF developers
Home Profile Tips 全記事一覧

ボックス=ミュラー法による正規分布の乱数生成

(2016/12/03 0:50:56 created.)

場合によっては一様分布ではなく、正規分布にしたがった乱数が必要となることがあります。そのようなときは、例えばボックス=ミュラー法による正規分布の乱数生成をおこないます。ボックス=ミュラー法についてはこちらなどを参考にしてください。

Program.cs
  1. namespace Tips_Random
  2. {
  3.     using System;
  4.     using System.Linq;
  5.  
  6.     class Program
  7.     {
  8.         static void Main(string[] args)
  9.         {
  10.             #region ボックス=ミュラー法による正規分布の乱数生成
  11.             Console.WriteLine("ボックス=ミュラー法による正規分布の乱数生成器を作る。");
  12.             Console.WriteLine("指定された平均値と標準偏差を持つ分布の乱数を生成できる。");
  13.             Console.WriteLine("output.csv に出力しているので Excel などでその分布が確認できる。");
  14.             var rand2 = new BoxMullerRandom();
  15.             var mu = 2.0;
  16.             var sigma = 0.27;       // だいたい ±1.0 の分布になる
  17.             using (var writer = new System.IO.StreamWriter("output.csv"))
  18.             {
  19.                 var NN = 1000;
  20.                 var boxValues = Enumerable.Range(0, NN).Select(_ =>
  21.                 {
  22.                     var value = rand2.Next(mu, sigma);
  23.                     writer.WriteLine(value);
  24.                     return value;
  25.                 }).ToArray();
  26.                 var boxAve = boxValues.Average();
  27.                 var boxStdEv = Math.Sqrt(boxValues.Select(x => x * x).Sum() / NN - boxAve * boxAve);
  28.                 Console.WriteLine("指定した値 : μ = {0}, σ = {1}", mu, sigma);
  29.                 Console.WriteLine("計算した値 : boxAve = {0}, σ = {1}", boxAve, boxStdEv);
  30.                 Console.WriteLine("実際の平均値と標準偏差が指定した値に近くなっている。");
  31.             }
  32.             Console.WriteLine("");
  33.             Console.ReadKey();
  34.             #endregion ボックス=ミュラー法による正規分布の乱数生成
  35.         }
  36.     }
  37.  
  38.     /// <summary>
  39.     /// ボックス=ミュラー法による正規分布の乱数生成器を表します。
  40.     /// </summary>
  41.     public class BoxMullerRandom
  42.     {
  43.         /// <summary>
  44.         /// 乱数生成器
  45.         /// </summary>
  46.         private Random _rand;
  47.  
  48.         /// <summary>
  49.         /// 新しいインスタンスを生成します。
  50.         /// </summary>
  51.         public BoxMullerRandom()
  52.             : this(Environment.TickCount)
  53.         {
  54.         }
  55.  
  56.         /// <summary>
  57.         /// 新しいインスタンスを生成します。
  58.         /// </summary>
  59.         /// <param name="seed">乱数生成用のシードを指定します。</param>
  60.         public BoxMullerRandom(int seed)
  61.         {
  62.             this._rand = new Random(seed);
  63.         }
  64.  
  65.         /// <summary>
  66.         /// 指定された分布にしたがった乱数を返します。
  67.         /// </summary>
  68.         /// <param name="mu">平均値を指定します。</param>
  69.         /// <param name="sigma">標準偏差を指定します。</param>
  70.         /// <param name="isGetCos">余弦波を用いる場合に true を指定します。</param>
  71.         /// <returns>指定された条件にしたがった乱数を返します。</returns>
  72.         public double Next(double mu = 0.0, double sigma = 1.0, bool isGetCos = true)
  73.         {
  74.             var rand = 0.0;
  75.             while ((rand = this._rand.NextDouble()) == 0.0) ;
  76.             var rand2 = this._rand.NextDouble();
  77.             var normRand = GetNormRandValue(rand, rand2, isGetCos);
  78.             return normRand * sigma + mu;
  79.         }
  80.  
  81.         private double GetNormRandValue(double rand, double rand2, bool isGetCos = true)
  82.         {
  83.             return Math.Sqrt(-2.0 * Math.Log(rand)) * (isGetCos ? Math.Cos(2.0 * Math.PI * rand2) : Math.Sin(2.0 * Math.PI * rand2));
  84.         }
  85.  
  86.         /// <summary>
  87.         /// 指定された分布に従った乱数の組を返します。
  88.         /// </summary>
  89.         /// <param name="mu">平均値を指定します。</param>
  90.         /// <param name="sigma">標準偏差を指定します。</param>
  91.         /// <returns>指定された条件にしたがった乱数の組を返します。</returns>
  92.         public double[] NextPair(double mu = 0.0, double sigma = 1.0)
  93.         {
  94.             var rand = 0.0;
  95.             while ((rand = this._rand.NextDouble()) == 0.0) ;
  96.             var rand2 = this._rand.NextDouble();
  97.             return new double[]
  98.             {
  99.                 GetNormRandValue(rand, rand2, true) * sigma + mu,
  100.                 GetNormRandValue(rand, rand2, false) * sigma + mu,
  101.             };
  102.         }
  103.     }
  104. }


実行結果より、指定した平均値と標準偏差にしたがった分布の乱数が発生している様子がうかがえます。