//****************************************************************************************************** // GaussianDistribution.cs - Gbtc // // Copyright © 2014, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 04/30/2014 - Stephen E. Chisholm // Generated original version of source code. // //****************************************************************************************************** //------------------------------------------------------------------------------------------------------ // Code base on Wikipedia article http://en.wikipedia.org/wiki/Box–Muller_transform //------------------------------------------------------------------------------------------------------ using System; namespace GSF.NumericalAnalysis { /// /// Implements a BoxMuller method for generating statistically normal random numbers. /// public class GaussianDistribution { #region [ Members ] // Fields private readonly Random m_random; private readonly double m_mean; private readonly double m_standardDeviation; private readonly double m_min; private readonly double m_max; private bool m_z1IsValid; private double m_z1; #endregion #region [ Constructors ] /// /// Creates a /// /// the mean of the distribution /// the standard deviation /// a clipping boundary /// a clipping boundary public GaussianDistribution(double mean, double standardDeviation, double min, double max) { standardDeviation = Math.Abs(standardDeviation); //These limits are set to prevent excessive looping when a value is calculated outside this range. if (min > mean - 0.25 * standardDeviation) throw new ArgumentOutOfRangeException(nameof(min), "must be less than 1/4 standard deviations away from the mean"); if (max < mean + 0.25 * standardDeviation) throw new ArgumentOutOfRangeException(nameof(max), "must be greater than 1/4 standard deviations away from the mean"); m_random = new Random(Guid.NewGuid().GetHashCode()); m_mean = mean; m_standardDeviation = standardDeviation; m_min = min; m_max = max; } #endregion #region [ Methods ] /// /// Gets the next random value. /// /// public double Next() { double value; TryAgain: if (m_z1IsValid) { m_z1IsValid = false; value = m_mean + m_standardDeviation * m_z1; } else { double u1 = m_random.NextDouble(); double u2 = m_random.NextDouble(); if (u1 < 1e-100) u1 = 1e-100; if (u2 < 1e-100) u1 = 1e-100; double sqrt = Math.Sqrt(-2 * Math.Log(u1)); double z0 = sqrt * Math.Sin(2 * Math.PI * u2); m_z1 = sqrt * Math.Cos(2 * Math.PI * u2); m_z1IsValid = true; value = m_mean + m_standardDeviation * z0; } if (value < m_min || value > m_max) goto TryAgain; return value; } #endregion } }