UVa 10886 Standard Deviation

题目大意

给定一个随机数生成器,其种子 seed646464 位无符号整数。生成器函数 gen() 每次调用会更新 seed 并返回一个 [0,1)[0, 1)[0,1) 范围内的浮点数。你需要计算用给定种子生成前 nnn 个数的标准差

输入
第一行是测试用例数 NNN(最多 404040 个)。每个测试用例一行,包含两个整数 nnn1≤n≤1071 \le n \le 10^71n107)和 seedseedseed0≤seed<2640 \le seed < 2^{64}0seed<264)。

输出
对每个测试用例,输出一行 Case #z: 后接标准差,保留 555 位小数。绝对误差不超过 10−410^{-4}104 视为正确。


生成器分析

题目给出的生成器代码如下(已转换为 C++\texttt{C++}C++ 风格):

unsigned long long seed;
long double gen() {
    static const long double Z = (long double)1.0 / (1ULL << 32);
    seed >>= 16;
    seed &= (1ULL << 32) - 1;
    seed *= seed;
    return seed * Z;
}

关键步骤解析

  1. Z = 1.0 / 2^{32},所以返回值范围是 [0,1)[0, 1)[0,1)
  2. seed >>= 16:右移 161616 位。
  3. seed &= (1ULL << 32) - 1:保留低 323232 位。
  4. seed *= seed:平方运算,可能导致溢出,但因为只保留低 646464 位,相当于模 2642^{64}264 乘法。
  5. 返回 seed * Z,即一个 [0,1)[0, 1)[0,1) 的浮点数。

这个生成器是一个伪随机数生成器,其序列由初始种子唯一确定。


标准差计算公式推导

标准差的定义:
σ=1n∑i=1n(xi−μ)2 \sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i - \mu)^2} σ=n1i=1n(xiμ)2
其中 μ=1n∑i=1nxi\mu = \frac{1}{n} \sum_{i=1}^{n} x_iμ=n1i=1nxi

展开平方项:
σ=1n∑i=1n(xi2−2μxi+μ2) \sigma = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (x_i^2 - 2\mu x_i + \mu^2)} σ=n1i=1n(xi22μxi+μ2)
=1n∑xi2−2μn∑xi+μ2n⋅n = \sqrt{\frac{1}{n} \sum x_i^2 - \frac{2\mu}{n} \sum x_i + \frac{\mu^2}{n} \cdot n} =n1xi2n2μxi+nμ2n
=1n∑xi2−2μ2+μ2 = \sqrt{\frac{1}{n} \sum x_i^2 - 2\mu^2 + \mu^2} =n1xi22μ2+μ2
=1n∑xi2−μ2 = \sqrt{\frac{1}{n} \sum x_i^2 - \mu^2} =n1xi2μ2

关键推导结果
σ=∑xi2n−(∑xin)2 \sigma = \sqrt{\frac{\sum x_i^2}{n} - \left(\frac{\sum x_i}{n}\right)^2} σ=nxi2(nxi)2

因此,我们只需维护两个累加量:

  • sumX∑xi\sum x_ixi
  • sumX2∑xi2\sum x_i^2xi2

计算时:

  1. 均值 mean = sumX / n
  2. 方差 variance = sumX2 / n - mean * mean
  3. 标准差 stdDev = sqrt(variance)

注意:由于浮点数计算误差,variance 可能出现负值(极小负数),此时应将其置为 000


算法设计

1. 直接计算法

  • 对于每个测试用例,初始化 seed
  • 循环 nnn 次调用 gen(),累加 sumXsumX2
  • 计算均值、方差、标准差。
  • 时间复杂度:O(n)O(n)O(n) 每用例。
  • 空间复杂度:O(1)O(1)O(1)(只存累加量)。

2. 数值稳定性问题

  • nnn 很大(10710^7107)时,sumXsumX2 可能非常大,但 long double(通常 808080 位或 128128128 位)足以应对。
  • 方差计算可能出现大数相减导致精度损失,但题目允许误差 10−410^{-4}104,可以接受。

3. 实现细节

  • 使用 long double 存储累加量和结果。
  • 输出保留 555 位小数。
  • 处理方差为负的情况(浮点误差)。

代码实现

// Standard Deviation
// UVa ID: 10886
// Verdict: Accepted
// Submission Date: 2025-12-01
// UVa Run Time: 0.410s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

unsigned long long seed; // 全局变量,gen() 会修改它

long double gen()
{
    static const long double Z = (long double)1.0 / (1ULL << 32);
    seed >>= 16;
    seed &= (1ULL << 32) - 1;
    seed *= seed;
    return seed * Z;
}

int main()
{
    int caseCount;
    cin >> caseCount;
    cout << fixed << setprecision(5);
    for (int caseId = 1; caseId <= caseCount; ++caseId)
    {
        int n;
        unsigned long long initSeed;
        cin >> n >> initSeed;
        seed = initSeed;
        long double sumX = 0.0, sumX2 = 0.0;
        for (int i = 0; i < n; ++i)
        {
            long double x = gen();
            sumX += x;
            sumX2 += x * x;
        }
        long double mean = sumX / n;
        long double variance = sumX2 / n - mean * mean;
        if (variance < 0) variance = 0; // 处理浮点误差
        long double stdDev = sqrt(variance);
        cout << "Case #" << caseId << ": " << stdDev << endl;
    }
    return 0;
}

复杂度分析

  • 时间复杂度O(∑n)O(\sum n)O(n),每个数生成一次,计算一次累加。
  • 空间复杂度O(1)O(1)O(1),只使用常数个变量。

对于最大的 n=107n = 10^7n=107,单个测试用例大约需要生成 10710^7107 个数,在合理时间内可以完成。


注意事项

  1. 浮点误差:方差计算可能出现极小负值,直接取 000 即可。
  2. 精度要求:使用 long double 提高精度,输出保留 555 位小数。
  3. 生成器细节:注意 seed 是全局变量,每个测试用例需要重新初始化。
  4. 输入输出:使用 cin/cout 并关闭同步可加快速度(本题未要求,但可用于优化)。

总结

本题是一个典型的流式计算统计量问题,核心在于利用标准差的等价公式:
σ=∑xi2n−(∑xin)2 \sigma = \sqrt{\frac{\sum x_i^2}{n} - \left(\frac{\sum x_i}{n}\right)^2} σ=nxi2(nxi)2
从而避免存储所有数据,只需在线更新两个累加量。同时,理解伪随机数生成器的工作原理也是解题关键。

### 样本标准差的计算 样本标准差是衡量数据集中样本值相对于样本均值的离散程度的一种统计量。其公式为: \[ s = \sqrt{\frac{1}{n-1} \sum_{i=1}^n (x_i - \bar{x})^2} \] 其中: - \( s \) 是样本标准差, - \( n \) 是样本的数量, - \( x_i \) 是第 \( i \) 个样本值, - \( \bar{x} \) 是样本均值。 在编程中,可以通过以下步骤实现样本标准差的计算[^2]。 ```python import math def calculate_sample_standard_deviation(data): if len(data) < 2: return None # 样本数量不足时无法计算标准差 mean = sum(data) / len(data) variance = sum((x - mean) ** 2 for x in data) / (len(data) - 1) return math.sqrt(variance) # 示例数据 data = [2, 4, 4, 4, 5, 5, 7, 9] sample_std_dev = calculate_sample_standard_deviation(data) print(f"Sample Standard Deviation: {sample_std_dev}") ``` 上述代码定义了一个函数 `calculate_sample_standard_deviation`,它接收一个列表 `data` 并返回样本标准差。注意,在分母中使用了 \( n-1 \),这是因为我们计算的是样本标准差而不是总体标准差。 此外,可以利用现成的库如 NumPy 来简化计算过程: ```python import numpy as np data = [2, 4, 4, 4, 5, 5, 7, 9] sample_std_dev = np.std(data, ddof=1) # ddof=1 表示使用样本标准差公式 print(f"Sample Standard Deviation using NumPy: {sample_std_dev}") ``` 在这里,`np.std` 函数通过设置参数 `ddof=1`(即 Delta Degrees of Freedom)来调整自由度以匹配样本标准差的公式。 ### 样本标准差的应用场景 样本标准差广泛应用于统计学和机器学习领域,例如异常值检测[^3]。如果某个特征的值超过其自身标准差的两倍,则可能被视为异常值。 ### 总结 样本标准差是一个重要的统计指标,用于评估数据的波动性。无论是手动实现还是借助第三方库,都可以方便地计算样本标准差。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值