65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 随机数生成器和分布

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (114投票s)

2006 年 8 月 9 日

20分钟阅读

viewsIcon

439306

downloadIcon

29293

提供了一个完全托管的类库,提供各种随机数生成器和分布

目录

引言

本文的想法诞生于 2004 年,当时我正在撰写我的学士论文,其中包括实现一个流量控制算法的模拟和测试环境。由于各种原因,我选择将其实现为一个使用 C# 开发的托管应用程序。我唯一后悔这个选择的时候是当我开始实现数据流量模拟时,这需要分布式的随机变量。不幸的是,我在 .NET 框架类库或任何其他资源中都找不到任何免费提供的随机数分布的托管实现。因此,我亲自实现了所需的随机数分布。从那时起,我一直有一个想法,即基于这些少量实现创建一个类库并在此处 CodeProject 上发布,但我一直没有真正有时间去实现它。直到现在。

随机类库

随机类库包含随机数生成器和随机数分布的抽象基类,以及从两者派生出的各种具体类。在我开始描述这些类之前,我必须提及,所有生成(分布式)随机数的算法都不是我的智力成果,因为我不是一位杰出的数学家。因此,本文以及源代码都包含对相关知识资源的引用。

随机数生成器

抽象基类“Generator”

Generator 类型声明了所有随机数生成器的通用功能。这包括 System.Random 类型提供的一个,以及一些扩展。因此,该类还额外声明了 NextDouble 方法的两个重载、一个 NextBoolean 方法以及重置随机数生成器的可能性。这在使用伪随机数生成器时非常有用。下表列出了所有抽象成员及其简要说明。

抽象成员 描述
bool CanReset { get; } 获取一个值,指示随机数生成器是否可以重置,以便它再次生成相同的随机数序列。
bool Reset(); 重置随机数生成器,使其再次生成相同的随机数序列。
如果随机数生成器已重置,则返回 true;否则,返回 false
int Next(); 返回一个小于 Int32.MaxValue 的非负随机数;也就是说,返回值范围包括 0 但不包括 Int32.MaxValue
int Next(
int maxValue);
返回一个小于指定最大值的非负随机数;也就是说,返回值范围包括 0 但不包括 maxValue
maxValue 必须大于或等于 0。
int Next(
int minValue,
int maxValue);
返回指定范围内的随机数;也就是说,返回值范围包括 minValue 但不包括 maxValue
maxValue 必须大于或等于 minValue
double NextDouble(); 返回一个小于 1.0 的非负浮点随机数;也就是说,返回值范围包括 0.0 但不包括 1.0。
double NextDouble(
double maxValue);
返回一个小于指定最大值的非负浮点随机数;也就是说,返回值范围包括 0.0 但不包括 maxValue
maxValue 必须大于或等于 0.0。
double NextDouble(
double minValue,
double maxValue);
返回指定范围内的浮点随机数;也就是说,返回值范围包括 minValue 但不包括 maxValue
maxValue 必须大于或等于 minValueminValuemaxValue 之间的距离必须小于或等于 Double.MaxValue
bool NextBoolean(); 返回一个随机布尔值。
void NextBytes(
byte[] buffer);
用随机数填充指定字节数组的元素。
字节数组的每个元素都被设置为大于或等于 0 且小于或等于 Byte.MaxValue 的随机数。

派生自“Generator”的类

目前,该库提供了四个从 Generator 派生出的类。它们在下表中列出,并附有简短描述和进一步阅读的链接。

实现 可重置 描述 / 链接
ALFGenerator true 表示一个附加滞后斐波那契伪随机数生成器,带有一些额外的 Next 方法。
此类型基于 Boost Random Number Library 中的实现。它使用模数 232,默认情况下使用“滞后”418 和 1279。这可以通过相关的 ShortLagLongLag 属性进行调整。一些流行的对在 维基百科 - 滞后斐波那契生成器 上有介绍。
MT19937Generator true 表示一个周期为 219937-1 的梅森旋转伪随机数生成器,带有一些额外的 Next 方法。
此类型基于 梅森旋转主页 上提供的信息和实现。
StandardGenerator true 表示一个简单的伪随机数生成器。
此类型内部使用 System.Random 类型的一个实例来生成伪随机数。
XorShift128Generator true 表示一个周期为 2128-1 的异或移位伪随机数生成器,带有一些额外的 Next 方法。
此类型基于 CP 文章“System.Random 的快速替代品”中提供的实现以及 George Marsaglia 在论文“异或移位 RNGs”中发表的异或移位随机数生成器的理论背景。

随机数分布

抽象基类“Distribution”

Distribution 类声明了所有随机数分布的通用功能。其必须由继承者实现的抽象成员是一些提供分布特征信息和 NextDouble 方法的属性。下表列出了所有抽象成员及其简要描述。

抽象成员 描述
double Minimum { get; } 获取分布式随机数的最小可能值。
double Maximum { get; } 获取分布式随机数的最大可能值。
double Mean { get; } 获取分布式随机数的平均值。
如果无法计算平均值,将返回 Double.NaN 常量。
double Median { get; } 获取分布式随机数的中位数。
如果无法计算中位数,将返回 Double.NaN 常量。
double Variance { get; } 获取分布式随机数的方差。
如果无法计算方差,将返回 Double.NaN 常量。
double[] Mode { get; } 获取分布式随机数的众数。
如果无法计算众数,将返回一个空数组。
double NextDouble(); 返回一个分布式浮点随机数。

除了其抽象成员外,Distribution 类型还提供了一些所有随机数分布共有的实现细节。由于分布式随机数的计算必然需要一个随机数生成器,因此 generator 字段存储着一个 Generator 类的实例。该实例可通过其各自的属性供继承者访问。此外,定义了两个 protected 构造函数:一个接受用户定义的 Generator 对象,另一个是应用 StandardGenerator 类型实例的标准构造函数。Distribution 类型还提供与 Generator 类相同的重置功能。实际上,它只是转发存储的 Generator 实例的结果,因为随机数分布只有在其底层随机数生成器可重置时才能重置。

public abstract class Distribution
{
    protected Generator Generator
    {
        get { return this.generator; }
        set { this.generator = value; }
    }
    
    private Generator generator;

    protected Distribution() : this(new StandardGenerator())
    {
    }

    protected Distribution(Generator generator)
    {
        if (generator == null)
        {
            string message = string.Format(null, 
                ExceptionMessages.ArgumentNull, "generator");
            throw new ArgumentNullException("generator", message);
        }
        this.generator = generator;
    }

    public bool CanReset
    {
        get { return this.generator.CanReset; }
    }

    public virtual bool Reset()
    {
        return this.generator.Reset();
    }
    
    ...
}

派生自“Distribution”的类

随机类库目前为各种连续和离散分布提供了 Distribution 的继承者。它们在下表中列出,并附有简短描述、进一步阅读的链接以及范围和特定分布参数的信息。

连续分布
实现 参数 / 范围 描述 / 链接
BetaDistribution
0 < α < ∞
0 < β < ∞
0 ≤ X ≤ 1
提供 Beta 分布随机数的生成。
此类型基于 维基百科 - Beta 分布Xycoon - Beta 分布 中提供的信息。
BetaPrimeDistribution
1 < α < ∞
1 < β < ∞
0 ≤ X < ∞
提供 Beta-Prime 分布随机数的生成。
此类型基于 Xycoon - 逆 Beta 分布 中提供的信息。
CauchyDistribution
-∞ < α < ∞
0 < γ < ∞
-∞ < X < ∞
提供 Cauchy 分布随机数的生成。
此类型基于 维基百科 - Cauchy 分布Xycoon - Cauchy 分布 中提供的信息。
ChiDistribution
α ∈ {1,2,...}
0 ≤ X < ∞
提供 Chi 分布随机数的生成。
此类型基于 维基百科 - Chi 分布 中提供的信息。
ChiSquareDistribution
α ∈ {1,2,...}
0 ≤ X < ∞
提供卡方分布随机数的生成。
此类型基于 维基百科 - 卡方分布 中提供的信息。
ContinuousUniformDistribution
α ≤ β < ∞
-∞ < α ≤ β
α ≤ X < β
提供连续均匀分布随机数的生成。
此类型基于 维基百科 - 均匀分布 (连续) 中提供的信息。
ErlangDistribution
0 < α < ∞
λ ∈ {1,2,...}
0 ≤ X < ∞
提供爱尔兰分布随机数的生成。
此类型基于 维基百科 - 爱尔兰分布Xycoon - 爱尔兰分布 中提供的信息。
ExponentialDistribution
0 < λ < ∞
0 ≤ X < ∞
提供指数分布随机数的生成。
此类型基于 维基百科 - 指数分布 中提供的信息。
FisherSnedecorDistribution
α ∈ {1,2,...}
β ∈ {1,2,...}
0 ≤ X < ∞
提供 Fisher-Snedecor 分布随机数的生成。
此类型基于 维基百科 - F 分布 中提供的信息。
FisherTippettDistribution
0 < α < ∞
-∞ < μ < ∞
-∞ < X < ∞
提供 Fisher-Tippett 分布随机数的生成。
此类型基于 维基百科 - Fisher-Tippett 分布 中提供的信息。
GammaDistribution
0 < α < ∞
0 < θ < ∞
0 ≤ X < ∞
提供 Gamma 分布随机数的生成。
此类型基于 维基百科 - Gamma 分布 中提供的信息。
LaplaceDistribution
0 < α < ∞
-∞ < μ < ∞
-∞ < X < ∞
提供 Laplace 分布随机数的生成。
此类型基于 维基百科 - Laplace 分布 中提供的信息。
LognormalDistribution
-∞ < μ < ∞
0 ≤ σ < ∞
0 ≤ X < ∞
提供对数正态分布随机数的生成。
此类型基于 维基百科 - 对数正态分布Boost Random Number Library 中的实现。
NormalDistribution
-∞ < μ < ∞
0 < σ < ∞
-∞ < X < ∞
提供正态分布随机数的生成。
此类型基于 维基百科 - 正态分布通信网络类库 中的实现。
ParetoDistribution
0 < α < ∞
0 < β < ∞
α ≤ X < ∞
提供 Pareto 分布随机数的生成。
此类型基于 维基百科 - Pareto 分布Xycoon - Pareto 分布 中提供的信息。
PowerDistribution
0 < α < ∞
0 < β < ∞
0 ≤ X ≤ 1/β
提供幂分布随机数的生成。
此类型基于 Xycoon - 幂分布 中提供的信息。
RayleighDistribution
0 < σ < ∞
0 ≤ X < ∞
提供瑞利分布随机数的生成。
此类型基于 维基百科 - 瑞利分布 中提供的信息。
StudentsTDistribution
ν ∈ {1,2,...}
-∞ < X < ∞
提供 t 分布随机数的生成。
此类型基于 维基百科 - 学生 t 分布Xycoon - 学生 t 分布 中提供的信息。
TriangularDistribution
-∞ < α < β
α < β < ∞
α ≤ γ ≤ β
α ≤ X ≤ β
提供三角分布随机数的生成。
此类型基于 维基百科 - 三角分布Boost Random Number Library 中的实现。
WeibullDistribution
0 < α < ∞
0 < λ < ∞
0 ≤ X < ∞
提供威布尔分布随机数的生成。
此类型基于 维基百科 - 威布尔分布 中提供的信息。

离散分布
(所有这些都额外实现了一个 Next 方法,该方法返回一个分布式随机数,即 32 位带符号整数。)
实现 参数 / 范围 描述
BernoulliDistribution
0 ≤ α ≤ 1
X ∈ {0,1}
提供伯努利分布随机数的生成。
此类型基于 维基百科 - 伯努利分布 中提供的信息。
BinomialDistribution
0 ≤ α ≤ 1
β ∈ {0,1,...}
X ∈ {0,1,...,β}
提供二项分布随机数的生成。
此类型基于 维基百科 - 二项分布 中提供的信息。
DiscreteUniformDistribution
α ∈ {...,β-1,β}
β ∈ {α,α+1,...}
X ∈ {α,α+1,...,β-1,β}
提供离散均匀分布随机数的生成。
此类型基于 维基百科 - 均匀分布 (离散) 中提供的信息。
GeometricDistribution
0 < α ≤ 1
X ∈ {1,2,...}
提供几何分布随机数的生成。
此类型基于 维基百科 - 几何分布通信网络类库 中的实现。
PoissonDistribution
0 < λ < ∞
X ∈ {0,1,...}
提供泊松分布随机数的生成。
此类型基于 维基百科 - 泊松分布通信网络类库 中的实现。

除了继承的成员之外,所有派生自 Distribution 的类还有一些共同的相似之处。首先,所有分布都提供两个构造函数:一个接受用户定义的 Generator 对象作为底层随机数生成器,另一个是标准构造函数,为此目的使用 StandardGenerator 类型的一个实例。

其次,每个分布都提供方法,允许您确定一个值对于其特定参数是否有效,因此可以通过所属属性进行分配。这些方法遵循命名方案“IsValid{parameter}”,并且为了保持一致性,对于值范围不受限制的参数也可用。

此外,从 Distribution 派生的类在可能的情况下利用 helper 变量来加速随机数生成。这些 helper 存储仅依赖于分布参数的中间结果,因此在 NextDouble 的后续执行中无需重新计算。helper 变量的计算封装在 UpdateHelperVariables 方法中,该方法在构造期间以及每当涉及 helper 计算的分布参数发生更改时被调用。以下代码片段取自 PoissonDistribution 类型,应能说明上述解释。

public class PoissonDistribution : Distribution
{
    public double lambda;
    private double helper1;

    public double Lambda
    {
        get { return this.lambda; }
        set
        {
            if (this.IsValidLambda(value))
            {
                this.lambda = value;
                this.UpdateHelpers();
            }
        }
    }

    public PoissonDistribution() : this(new StandardGenerator())
    {
    }

    public PoissonDistribution(Generator generator) : base(generator)
    {
        this.lambda = 1.0;
        this.UpdateHelpers();
    }

    public bool IsValidLambda(double value)
    {
        return value > 0.0;
    }

    private void UpdateHelpers()
    {
        this.helper1 = Math.Exp(-1.0 * this.lambda);
    }

    public double NextDouble()
    {
        double count = 0;
        for (double product = 
            this.generator.NextDouble(); product >= this.helper1; 
            product *= this.generator.NextDouble())
        {
            count++;
        }

        return count;
    }
    
    ...

版本历史

1.4
  • Distribution.Reset 方法更改为 virtual,以便可以在派生类中重写
  • 重写了 NormalDistribution 中的 Reset 方法:此重写会丢弃一个已计算的、待返回的随机数——底层生成算法总是同时计算两个随机数——因此该分布总是正确重置
  • 重写了 BetaDistributionBetaPrimeDistributionChiDistributionChiSquareDistributionFisherSnedecorDistributionLogNormalDistributionRayleighDistributionStudentsTDistribution 中的 Reset 方法:此重写明确重置了各自的底层分布(在大多数情况下是 NormalDistribution),因此列出的分布总是正确重置
1.3
  • 修复了 NormalDistribution 中的 bug:现在对参数 μ 和 σ 的更改会丢弃一个已计算的、待返回的随机数——底层生成算法总是同时计算两个随机数——因此在任何情况下,参数更改都会从第一个生成的随机数开始反映
1.2
  • 将字段 Distribution.generator 的访问修饰符从 protected 更改为 private,并通过新的 protected 属性 Distribution.Generator 使其可访问
    Distribution 的所有继承者适应上述更改
    有关进一步解释,请点击链接:Visual Studio Team System - 不要声明可见实例字段
  • ALFGeneratorMT19937GeneratorStandardGeneratorXorShift128Generator 类型的重新初始化代码从它们的 virtual Reset 方法移动到新的 private ResetGenerator 方法中,以便可以安全地从它们的构造函数(和 Reset)中调用
    有关进一步解释,请点击链接:Visual Studio Team System - 不要在构造函数中调用可重写方法
  • 调整了 ALFGeneratorMT19937GeneratorXorShiftGenerator 类型的 NextBytes 方法,使其检查传入的缓冲区是否为空引用,并在需要时抛出 ArgumentNullException
  • 所有异常都使用本地化消息实例化;目前提供英语、德语、西班牙语和法语翻译
1.1
  • 添加了伯努利、Beta-prime、二项式、Chi、卡方、爱尔兰、Fisher-Snedecor、Fisher-Tippett、拉普拉斯、瑞利、学生 t 和威布尔随机分布
  • 添加了加性滞后斐波那契伪随机数生成器
  • LognormalDistributionNormalDistribution 参数 μ 的属性名称从 My 改为 Mu,以统一使用英文名称
  • 修复了 PowerDistribution 中的 bug:参数 α 的更改现在反映在生成的随机数中
  • 调整了 ExponentialDistribution.NextDouble,以避免 Math.Log(0.0)
  • 改进了 MT19937Generator.NextXorShift128Generator.Next 在范围 > Int32.MaxValue 时指定范围的性能
1.0
  • 初始发布

版权声明

随机类库是自由软件。您可以根据自由软件基金会发布的 GNU 宽通用公共许可证(版本 2.1 或您选择的任何更高版本)的条款重新分发和/或修改它。本库的发布是为了希望它有用,但不提供任何担保,甚至不包括适销性或特定用途适用性的默示担保。有关更多详细信息,请参阅 GNU 宽通用公共许可证。您应该已随本库收到一份 GNU 宽通用公共许可证的副本。如果未收到,请写信给 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA。

RandomTester 应用程序

我开始开发 RandomTester 应用程序是在我为学士论文实现第一个随机数分布时。当时,它唯一的目的是可视化生成的随机数的分布以及特定分布参数对其的影响。在开发随机类库的过程中,我完善了这种可视化,并添加了随机数生成器和分布的性能测试。

测试随机数生成器

RandomTester 应用程序允许您测试和比较随机数生成器的性能。下图显示了运行此测试后的用户界面。

Test Random Number Generators

所有派生自 Generator 的类都列在左侧。它们可以通过单击各自列表条目逐个选择/取消选择,也可以通过使用“全选”或“全不选”按钮一次性选择/取消选择。在这些按钮下方,您可以指定每个生成器必须生成多少个样本来基准测试它们的性能。最后,必须选择应该测试哪些 Next 方法——由 Generator 类型声明并在派生类中实现。随机数生成器的性能以每秒调用次数衡量。结果显示在一个数据网格中,该数据网格包含每个随机数生成器的一行和每个测试的 Next 方法的一列。

测试随机数分布

随机数分布可以通过两种方式进行测试。首先,可以进行性能测试,类似于为随机数生成器提供的性能测试。其次,可以可视化生成的随机数的分布以及特定分布参数对其的影响。这些测试的用户界面如下图所示。

Test Random Number Distributions

如前所述,性能测试类似于随机数生成器基准测试。在左侧,列出了所有派生自 Distribution 的类,可以通过单击各自的列表条目逐个选择/取消选择,也可以通过使用“全选”或“全不选”按钮一次性选择/取消选择。此外,可以指定每个分布在基准测试期间必须生成的样本数量,以及一个底层随机数生成器。后一个调整提供了使用派生自 Generator 的类或分布默认值。如果选择了 Generator 的继承者,则使用其构造函数实例化测试的分布,该构造函数接受此类对象。否则,使用标准构造函数。所选随机数分布的性能表示为生成指定数量样本所需的时间。结果以升序显示在文本框中。

与两种性能测试不同,可视化测试的主要焦点不是性能,而是测试单个随机数分布。因此,它采用 ZedGraphControl 来显示生成的随机数分布的直方图,即不同值出现的频率,或者更准确地说,它们出现的可能性(概率密度函数)。

对于离散分布,这可以通过计算离散值的出现次数并除以生成的样本总数轻松完成。不幸的是,大多数随机数分布是连续的,并且具有相当大的范围。因此,给定值被多次生成的概率非常低,即使生成了许多数字。这就是为什么可视化测试将生成的样本域划分为指定数量的区间,然后显示这些区间内的值出现的可能性。对于离散随机数分布也使用这样的直方图,因为只要区间数量等于或大于离散值数量,它就会提供相同的结果。在这种情况下,分布类型之间的复杂区分变得冗余。

在左上角,一个下拉列表允许您从所有派生自 Distribution 的类中选择要可视化的分布。在该列表下方,一个分组框显示所选分布的当前特征。第二个分组框允许您调整特定参数。根据所选分布,参数的更改还会导致其一个或多个特征发生更改。与分布直接相关的最终调整是选择一个底层随机数生成器,该生成器允许您选择分布默认值或派生自 Generator 的类。

任何其他设置都与随机数分布的可视化有关。您可以指定用于创建直方图的样本和分段数量,直方图曲线是否应该平滑,以及是否使用特定边界(即任何超出边界的生成随机数将被忽略)。
如上图所示,可以同时显示多个直方图。这允许您检查参数对随机数分布的影响,甚至比较不同的分布。每个新生成的直方图都作为单独的曲线绘制。测试分布的名称和参数值会添加到图例中。除了直方图之外,左下角的文本框还显示了生成随机数所需的时间,以及它们的最小值、最大值、平均值和方差。

版本历史

1.2
  • 调整了分布可视化,使直方图的最后一个区间正确显示
    到目前为止,直方图图表由表示直方图区间最小边界的点组成,因此最后一个区间实际上并未绘制。因此,图表现在包含最后一个区间的最大边界的额外点,当然,该点的 y 值与相应的最小边界相同。
1.1
  • 在生成器测试中显示单位“samples/s”
  • 使用 byte[64] 测试 Generator.NextBytes 方法,以减少测试时间
1.0
  • 初始发布

版权声明

RandomTester 是自由软件。您可以根据自由软件基金会发布的 GNU 通用公共许可证(版本 2 或任何更高版本)的条款重新分发和/或修改它。本程序是希望它有用而发布,但不提供任何担保,甚至不包括适销性或特定用途适用性的默示担保。有关更多详细信息,请参阅 GNU 通用公共许可证。您应该已随本程序收到一份 GNU 通用公共许可证的副本;如果未收到,请写信给 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA。

参考文献

  1. 维基百科 - 随机数生成器
  2. 梅森旋转主页
  3. System.Random 的快速替代品
  4. George Marsaglia 撰写的论文《Xorshift RNGs
  5. 维基百科 - 概率分布
  6. MathWorld - 统计分布
  7. Xycoon - 统计分布
  8. Boost 随机数库
  9. 通信网络类库
  10. 一个灵活的 .NET 图表库

文章历史

  • 2007 年 5 月 29 日 - 文章已编辑并发布到 CodeProject.com 主文章库
© . All rights reserved.