This commit is contained in:
Thorsten Sommer 2020-09-26 00:06:14 +02:00
parent cde75c557d
commit 9eda579197
4 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,36 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace FastRng.Distributions
{
public sealed class Exponential : IDistribution
{
private double mean = 1;
public IRandom Random { get; set; }
public double Mean
{
get => this.mean;
set
{
if(value <= 0)
throw new ArgumentOutOfRangeException(message: "Mean must be greater than 0", null);
this.mean = value;
}
}
public async Task<double> GetDistributedValue(CancellationToken token)
{
if (this.Random == null)
return 0;
if(this.Mean == 1)
return -Math.Log(await this.Random.GetUniformDouble(token));
else
return this.Mean * -Math.Log(await this.Random.GetUniformDouble(token));
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace FastRng.Distributions
{
public sealed class Gamma : IDistribution
{
private double shape = 1;
public IRandom Random { get; set; }
public double Shape
{
get => this.shape;
set
{
if(value <= 0)
throw new ArgumentOutOfRangeException(message: "Shape must be greater than 0", null);
this.shape = value;
}
}
public double Scale { get; set; } = 1;
public async Task<double> GetDistributedValue(CancellationToken token)
{
if (this.Random == null)
return 0;
// Implementation based on "A Simple Method for Generating Gamma Variables"
// by George Marsaglia and Wai Wan Tsang. ACM Transactions on Mathematical Software
// Vol 26, No 3, September 2000, pages 363-372.
if (shape >= 1.0)
{
var distNormal = new Normal();
var d = shape - 1.0 / 3.0;
var c = 1.0 / Math.Sqrt(9.0 * d);
while(true)
{
double x, v;
do
{
x = await this.Random.NextNumber(0, 1, distNormal, token);
v = 1.0 + c * x;
}
while (v <= 0.0);
v = v * v * v;
var u = await this.Random.GetUniformDouble(token);
var xSquared = x * x;
if (u < 1.0 - 0.0331 * xSquared * xSquared || Math.Log(u) < 0.5 * xSquared + d * (1.0 - v + Math.Log(v)))
return this.Scale * d * v;
}
}
else
{
var dist = new Gamma{ Scale = 1, Shape = 1 + this.Shape};
var g = await this.Random.NextNumber(0.0f, 1.0f, dist, token); // TODO: Use double
var w = await this.Random.GetUniformDouble(token);
return this.Scale * g * Math.Pow(w, 1.0 / this.Shape);
}
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace FastRng.Distributions
{
public sealed class Normal : IDistribution
{
private double standardDeviation = 1;
public IRandom Random { get; set; }
public double Mean { get; set; } = 0;
public double StandardDeviation
{
get => this.standardDeviation;
set
{
if(value <= 0)
throw new ArgumentOutOfRangeException(message: "Standard deviation must be greater than 0", null);
this.standardDeviation = value;
}
}
public async Task<double> GetDistributedValue(CancellationToken token = default)
{
if (this.Random == null)
return 0;
var u1 = await this.Random.GetUniformDouble(token);
var u2 = await this.Random.GetUniformDouble(token);
var r = Math.Sqrt(-2.0 * Math.Log(u1));
var theta = 2.0 * Math.PI * u2;
var value = r * Math.Sin(theta);
return this.Mean + this.StandardDeviation * value;
}
}
}

View File

@ -0,0 +1,12 @@
using System.Threading;
using System.Threading.Tasks;
namespace FastRng.Distributions
{
public sealed class Uniform : IDistribution
{
public IRandom Random { get; set; }
public async Task<double> GetDistributedValue(CancellationToken token = default) => this.Random == null ? 0 : await this.Random.GetUniformDouble(token);
}
}