【问题标题】:Calculating variance with large numbers用大数计算方差
【发布时间】:2017-03-14 09:09:38
【问题描述】:

我并没有真正使用过方差计算,我也不知道会发生什么。其实我数学一点都不好。

我有一个包含 0-10000 范围内的 1000000 个随机数值的数组。

数组可能会变得更大,所以我使用 64 位 int 进行求和。

我试图找到关于如何计算方差的代码,但我不知道我是否得到正确的输出。

平均值为 4692,中位数为 4533。我使用以下代码得到方差 1483780.469308:

// size is the element count, in this case 1000000
// value_sum is __int64

double p2 = pow( (double)(value_sum - (value_sum/size)), (double)2.0 );
double variance = sqrt( (double)(p2 / (size-1)) );

我得到了合理的价值吗?

计算有什么问题吗?

【问题讨论】:

  • 2.0 是双精度;你不需要投射它。由于 p2 是双精度数,因此您不需要在 sqrt() 的参数中进行强制转换。在不需要时避免强制转换——它们有时是必要的,但好的代码很少包含它们。

标签: c++ math statistics largenumber variance


【解决方案1】:

注意:看起来你不是在计算方差。

通过从每个元素中减去平均值并计算这些差异的加权和来计算方差。

所以你需要做的是:

// Get mean
double mean = static_cast<double>(value_sum)/size;

// Calculate variance
double variance = 0;
for(int i = 0;i<size;++i) 
{
  variance += (MyArray[i]-mean)*(MyArray[i]-mean)/size;
}

// Display
cout<<variance;

请注意,这是样本方差,当基础分布未知时使用(因此我们假设均匀分布)。

另外,经过一番挖掘,我发现这不是一个无偏估计量。 Wolfram Alpha 对此有话要说,但作为一个例子,当MATLAB 计算方差时,它会返回“偏差校正样本方差”。

偏置校正方差可以通过每个元素除以size-1得到,或者:

//Please check that size > 1
variance += (MyArray[i]-mean)*(MyArray[i]-mean)/(size-1); 

还要注意,mean 的值保持不变。

【讨论】:

  • 请注意,在这种情况下,您可以“分解”除以大小,即在循环之后只除以大小一次,即公式中有它的原因是它代表概率并不总是像这里一样恒定。
  • @Jacob:谢谢!使用 0-10000 范围内的 100000 个值,平均值为 4690,我得到方差 ~8700000。这合理吗?这个值告诉我什么?
  • 感谢专家的意见 :) - 但是当我在 MATLAB 上尝试时,他们将 SSD 标准化为 size-1,所以我觉得在这里提及这一点很重要。此外,我决定在循环内进行划分以保持值尽可能小,但你是对的 - 我想这不是必需的。
  • @Jacob:好的,现在我明白了。感谢您的精彩回答!
  • @R.A - 该值似乎合理,表明您的数据存在很大差异 - 这意味着您在数组中的值分布与平均值“相差很大” - 我从 0- 生成了 10000 个随机数10000(使用 MATLAB),平均值约为 5028,方差约为 8252003
【解决方案2】:

首先,如果您只是想了解什么是“合理”方差,请记住,方差基本上是标准差的平方。标准差粗略地衡量从数据点到其预期值的典型距离。

因此,如果您的数据的平均值为 4692,并且计算得出的方差为 1483780,则意味着您的标准差约为 1218,这表明您的数字往往在 3474 - 5910 范围内。所以如果您的数字范围是 0 - 10000,那么这种差异实际上对我来说似乎有点低;但这显然取决于您的数据分布。

至于计算本身:您可以使用Welford's Method 在第一次读取数据时使用运行计算来计算方差(您不必提前知道平均值):

初始化 M1 = x1 和 S1 = 0。

对于后续的 x,使用递归 公式

Mk = Mk-1+ (xk - Mk-1)/k Sk = Sk-1 + (xk - Mk-1)*(xk - Mk)。

对于 2 ≤ k ≤ n,第 k 个估计 方差为 s2 = Sk/(k - 1)。

【讨论】:

    【解决方案3】:

    只是为了好玩,使用 std::valarray 而不是 std::vector 和(各种)算法获得相同结果的路径略有不同:

    template <class T>
    T const variance(std::valarray<T> const &v) {
        if (v.size() == 0)
            return T(0.0);
        T average = v.sum() / v.size();
        std::valarray<T> diffs = v-average;
        diffs *= diffs;
        return diffs.sum()/diffs.size();
    }
    

    正如 Jacob 所暗示的,方差计算实际上有两种可能的版本。就目前而言,这假设您的输入是“宇宙”。如果您只采集了整个宇宙的样本,则最后一行应使用:(diffs.size()-1) 而不是 diffs.size()

    【讨论】:

    • 酷。我遇到的第一个有用的 valarray 应用
    【解决方案4】:

    也许使用不同的公式?

    #include <functional>
    #include <algorithm>
    #include <iostream>
    int main()
    {
     using namespace std;
    
     vector<double> num( 3 );
     num[ 0 ] = 4000.9, num[ 1 ] = 11111.221, num[ 2 ] = -2;
    
    
     double mean = std::accumulate(num.begin(), num.end(), 0.0) / num.size();
     vector<double> diff(num.size());
     std::transform(num.begin(), num.end(), diff.begin(), 
                    std::bind2nd(std::minus<double>(), mean));
     double variance = std::inner_product(diff.begin(), diff.end(), 
                                         diff.begin(), 0.0) / (num.size() - 1);
     cout << "mean = " << mean << endl
          << "variance = " << variance << endl;
    }
    

    输出: 平均值 = 5036.71 方差 = 3.16806e+07

    【讨论】:

    • 这可能很好,但如果有人让我解释我的代码,我什至不知道从哪里开始......
    • 与 Jacob 的想法相同,只是使用 STL 和 vectors。让生活变得轻松:)
    【解决方案5】:

    样本方差计算:

    #include <math.h>
    #include <vector>
    
    double Variance(std::vector<double>);
    
    int main()
    {
         std::vector<double> samples;
         samples.push_back(2.0);
         samples.push_back(3.0);
         samples.push_back(4.0);
         samples.push_back(5.0);
         samples.push_back(6.0);
         samples.push_back(7.0);
    
         double variance = Variance(samples);
         return 0;
    }
    
    double Variance(std::vector<double> samples)
    {
         int size = samples.size();
    
         double variance = 0;
         double t = samples[0];
         for (int i = 1; i < size; i++)
         {
              t += samples[i];
              double diff = ((i + 1) * samples[i]) - t;
              variance += (diff * diff) / ((i + 1.0) *i);
         }
    
         return variance / (size - 1);
    }
    

    【讨论】:

      【解决方案6】:

      由于您正在处理大量数字,然后对它们进行浮点运算,您可能希望在双精度中执行所有操作;这将为您节省大量演员表。

      使用pow .. 2 计算平方似乎有点尴尬。你可以先计算你的数字,然后再乘以它自己得到一个平方。

      如果您在进行除法运算并且觉得需要进行转换,请将 操作数(即分子和/或分母)转换为 double 而不是结果。如果你除整数,你会失去准确性。

      我不确定您的方差公式是否正确。例如,您可能想查看 Wikipedia 中的解释。但我也不是数学专家,所以我不确定你有没有搞错。

      【讨论】:

      • 没有令人信服的理由在双精度算术中进行求和计算;输入数据最大的数字约为 1014,对于引用的值范围和数据集大小具有一万倍的安全系数。
      • 嘘,但也没有令人信服的理由不这样做,尤其是如果您设法避免使原本简单的程序复杂化。
      【解决方案7】:

      由于方差是标准差的平方,SO 1174984 的答案应该会有所帮助。简短的诊断是您需要计算值的平方和以及值的总和,而您似乎没有这样做。

      由于您有 106 个值,并且任何值的平方最高可达 108,因此您最终得到的平方和最高可达 1014;您的 64 位整数最多可以存储 1018,因此您仍然可以处理一万倍的输入,或范围高达一百万而不是一万的值,而不会发生溢出。因此,没有迫切需要转向纯双重计算。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-25
        • 1970-01-01
        • 2020-01-10
        • 1970-01-01
        • 2020-01-28
        • 1970-01-01
        • 2021-11-28
        相关资源
        最近更新 更多