C++ std::chi_squared_distribution

2025年2月11日 | 阅读 9 分钟

引言

在统计学和概率论的世界里,**卡方(χ²)** 分布是一个非常重要的概念,它在假设检验、置信区间估计和拟合优度检验等方面有着广泛的应用。在 C++ 中,我们可以通过标准库的 std::chi_squared_distribution 类来生成服从卡方分布的随机数。本文旨在全面理解 **std::chi_squared_distribution** 类,其用法,并展示实际示例,以说明如何在统计模拟和分析中使用它。

问题陈述

在许多科学和工程领域,都需要对遵循特定统计分布的数据进行建模和模拟。卡方分布在进行各种统计检验时尤其重要。从头开始构建此函数将非常繁琐。因此,需要一种有效的方法来生成卡方随机数。C++ 中的 std::chi_squared_distribution 类提供了这样一种实现,它以统一的方式可靠地完成了这一任务。

std::chi_squared_distribution 概述

在 C++ 中,**std::chi_squared_distribution** 类是标准库中用于生成服从卡方分布的随机数的类之一。该分布的形状取决于自由度(ν),自由度应为正整数。自由度决定了该分布的形状,而该类的对象生成的随机数可用于不同的统计过程。

1. 类定义

类定义如下:

2. 成员函数

std::chi_squared_distribution 类提供了几个成员函数,包括:

  • **构造函数:** 用指定的自由度初始化分布。
  • **operator():** 使用提供的随机数引擎生成服从卡方分布的随机数。
  • **param() / param() const:** 用于访问和修改分布参数。
  • **min() / max():** 返回分布可能生成的最小值和最大值。
  • **reset():** 重置分布的内部状态。

示例用法

下面是一个演示 std::chi_squared_distribution 如何在实践中应用的示例。

程序 1

输出

 
0.495524
0.199223
1.70424
2.96357
0.604156
1.6931
3.84347
2.21008
1.54623
2.8006   

说明

我们首先包含必要的头文件,并初始化一个名为 **std::mt19937** 的随机数生成器。之后,我们创建一个具有 4 作为参数的卡方分布对象。最后,我们通过一个 for 循环生成十个随机数并输出。

  1. 包含头文件
    • 该程序包含了 iostream 和 random,并利用了这些库。iostream 允许输入和输出操作,特别是将结果打印到控制台,而 random 提供了与生成随机数相关的各种功能,如卡方分布和随机数引擎。
  2. 随机数生成器初始化
    • **随机设备:** std::random_device 对象用于使用熵来播种随机数生成器。
    • **梅森旋转引擎:** 我们创建了一个 std::mt19937 对象。这是一个高质量的伪随机数生成器,使用 std::random_device 的值进行播种。
  3. 卡方分布定义
    • **分布定义:** std::chi_squared_distribution 对象以参数 4 创建,该参数代表分布的自由度。此参数决定了卡方分布的形状。
  4. 生成和显示随机数
    • **数字生成循环:** 一个循环会运行 10 次,生成并输出 10 个从卡方分布中抽取的随机数。
    • **数字生成:** 在这里,我们用给定的随机数生成器调用我们的分布对象。它会生成一个服从具有四度自由度的卡方分布的随机数。
  5. **输出:** 使用 std::cout 将每个生成的数字输出到控制台。

复杂度分析

时间复杂度

  1. 随机数生成器初始化
    • 初始化 std::random_device 和 std::mt19937 的操作以 O(1) 时间完成。
  2. 分布初始化
    • 使用 4 个自由度初始化 std::chi_squared_distribution 是一个恒定时间操作,O(1)。
  3. 生成随机数
    • 循环运行 10 次以生成随机数,因此其时间复杂度为 O(10),由于迭代次数是恒定的,因此简化为 O(1)。
    • 单次调用 chi_dist(gen) 可以在 O(1) 时间内完成。
  4. 输出操作
    • 每个数字的打印发生在每次迭代中,耗时相同。例如,如果有十次迭代,那么总打印时间将是 O(10),但可以简单地表示为 O(1)。

**总时间复杂度:** O(1),因为迭代次数(10)是恒定的。

空间复杂度

  1. 随机数生成器和分布对象
    • 这意味着 std::random_device、std::mt19937 和 std::chi_squared_distribution 对象只占用一定量的内存,且不超过特定大小,具体取决于其数据类型(O)。
  2. 存储生成数字的空间
    • 该程序不将生成的数字存储到任何容器中;它直接输出它们,因此不需要额外的存储空间。

**总空间复杂度:** O(1),因为没有使用随输入大小增长的动态数据结构。

程序 2

输出

 
Chi-squared distribution with 2 degrees of freedom:
1.08942 0.760881 1.28851 0.928611 0.749271 0.412491 6.75131 2.7623 4.184 1.67682 

Chi-squared distribution with 5 degrees of freedom:
2.5123 12.7979 2.34895 12.2529 2.01745 5.40017 2.13986 2.16705 3.11383 0.968784 

Chi-squared distribution with 10 degrees of freedom:
6.93881 13.5531 11.9684 10.6306 7.88249 6.85001 13.8348 10.1842 14.8602 13.1529   

说明

  1. 包含头文件
    • **<iostream>:** 用于输入和输出操作。
    • **<random>:** 用于随机数生成工具。
    • **<vector>:** 用于存储生成的数字。
  2. 生成卡方数字的函数
    • **函数定义:** generateChiSquaredNumbers 需要两个参数:degrees_of_freedom(自由度)和 count(数量)。
    • **随机数生成器:** std::random_device 初始化一个 std::mt19937 随机数生成器。
    • **卡方分布:** 使用指定的自由度实例化一个 std::chi_squared_distribution 对象。
    • **数字生成:** 一个循环生成指定数量的服从卡方分布的随机数,并将它们保存在一个名为 std::vector 的向量中。
    • **输出:** 该函数将生成的数字打印到标准输出设备(控制台)。
  3. 主函数
    • 调用 generateChiSquaredNumbers 三次,每次使用不同的自由度值(2、5 和 10),并且每次 count = 10。
    • 这展示了卡方分布在不同自由度下如何获得不同的形状。

复杂度分析

时间复杂度

  1. 随机数生成器初始化
    • 初始化 std::random_device 和 std::mt19937 具有恒定的时间复杂度,即 O(1)。
  2. 分布初始化
    • 同样,使用给定的自由度初始化 std::chi_squared_distribution 也只需要固定的时间 O(1)。
  3. 生成随机数
    • 生成 count 个随机数的循环具有线性时间复杂度 O(n),其中 n 代表函数中的 count(要生成的随机数数量)。
    • 每次调用 chi_dist(gen) 应该花费略少于一个单位的时间,O(1),假设高效的随机数生成,这通常是常数时间。
  4. 将数字存储到向量中
    • 平均而言,将每个生成的数字推送到 std::vector 中,每次插入需要 O(1) 时间。然而,考虑到可能的重新分配,n 次插入的摊销时间复杂度为 O(n)。
  5. 输出操作
    • 打印数字的循环也相对于打印的数字数量(n)具有线性时间。
    • 因此,generateChiSquaredNumbers 函数在生成和显示 count 个随机数时,总时间复杂度为 O(n)。

空间复杂度

  1. 随机数生成器和分布对象
    • std::random_device、std::mt19937 和 std::chi_squared_distribution 对象占用固定空间。因此,它们的时间复杂度为 O(1)。
  2. 用于存储数字的向量
    • 其空间复杂度为 O(n),因为 std::vector 存储 count 个数字。

程序 3

输出

 
Degrees of Freedom: 2
Mean: 1.94398
Variance: 3.96098
-----------------------------
Degrees of Freedom: 4
Mean: 3.91569
Variance: 6.90869
-----------------------------
Degrees of Freedom: 6
Mean: 5.93509
Variance: 12.4783
-----------------------------
Degrees of Freedom: 8
Mean: 7.89465
Variance: 16.2433
-----------------------------
Degrees of Freedom: 10
Mean: 9.89575
Variance: 19.3912
-----------------------------   

说明

  1. 包含头文件
    • **<iostream>:** 用于输入和输出操作。
    • **<random>:** 用于随机数生成。
    • **<vector>:** 用于存储生成的数字。
    • **<map>:** 用于将自由度映射到生成的数字。
    • **<numeric>:** 用于数值运算,如 std::accumulate。
    • **<cmath>:** 用于数学运算。
  2. 辅助函数
    • **calculateMean:** 计算数字向量的平均值。
    • **calculateVariance:** 在给定平均值的情况下,计算数字向量的方差。
  3. 生成卡方数字
    • **generateChiSquaredNumbers:** 它生成一个 std::vector,其中包含指定自由度的卡方分布生成的 count 个随机数。
  4. 主函数
    • 定义一组自由度值和为每个自由度生成的随机数数量。
    • 使用一个 map 来存储生成的数字,其中键是自由度。
    • 遍历自由度值,生成数字,然后将它们存储在 map 中。
    • 它计算并显示每个自由度下生成数字的平均值和方差。

复杂度分析

时间复杂度

  • **对于随机数生成器初始化:** 每个生成器 O(1)。
  • **随机数生成:** 生成 count 个数字,为每个自由度重复此操作,时间复杂度为 O(n)。因此,总时间复杂度为 O(d * n),其中 d 表示不同的自由度。
  • **计算平均值和方差:** 对于所有 count 个数字,计算其平均值和方差需要 O(n),乘以 d 后,总时间复杂度为 O(d * n)。
  • 最终时间复杂度:O(d * n)

空间复杂度

  • **随机数生成器和分布对象:** 每个对象 O(1)。
  • **将数字存储到向量中:** 每组 count 个数字占用 O(n) 空间。因此,总空间复杂度可以表示为 O(d * n)。
  • 最终空间复杂度:O(d * n)

应用

std::chi_squared_distribution 类有几种应用:

  • **假设检验:** 在统计学中,卡方分布用于确定观测数据的显著性。
  • **拟合优度检验:** 卡方分布用作模型与数据之间拟合优度的度量。
  • **置信区间估计:** 用于构建总体方差的置信区间。

结论

在 C++ 中,**std::chi_squared_distribution** 类是生成具有卡方分布的随机变量的有效工具。该类使开发人员能够轻松地进行统计模拟和分析,而无需处理复杂的分布生成问题。理解其函数和用法对于使用 C++ 进行正确的统计计算至关重要。