【问题标题】:What is the fastest way to calculate min, max, mean, median and standard deviation from C# array?从 C# 数组计算最小值、最大值、平均值、中值和标准差的最快方法是什么?
【发布时间】:2022-01-27 19:14:45
【问题描述】:

我有一个更大的数组或未排序的双精度列表,我想以最有效的方式计算最小值、最大值、平均值、中值和标准差。当然,我可以简单地使用 Linq 来逐个计算,但我认为可以更快。示例代码:

var list = new List<double>(){1.0, 2.5, 0.11, 0.7, 8.2, 3.4, 1.0};
var (min, max, mean, median, std) = CalculateMetrics(list);

private (double, double, double, double, double) CalculateMetrics(List<double> list) {
    // TODO
}

那么最有效的方法是什么?使用库对我来说也很好。

【问题讨论】:

  • 当你说“更大”时,比如多大? 500? 5k? 5M? 5B?如果您估计了潜在的数组大小,则可以选择更好的算法。考虑到所有相关的开销,可能不需要对大小约为 1k 的原始数组进行大量优化。

标签: c# max mean min standard-deviation


【解决方案1】:

您想要的所有描述性统计除了中位数都可以通过您的list 一次性计算出来。获得标准差的诀窍是累积样本的总和和平方和。这是一个例子。

int count = 0;
double sum = 0.0;
double sumsq = 0.0;
double max = double.MinValue;
double min = double.MaxValue;

foreach (double sample in list)
{
    count++;
    sum += sample;
    sumsq += sample * sample;
    if (sample > max) max = sample;
    if (sample < min) min = sample;
}

double mean = sum / count;
double stdev = Math.Sqrt((sumsq / count) - (mean * mean));

因为这只会通过列表一次,所以它适用于任何IEnumerable 样本集合,并且与 LINQ 兼容。

显然,这是快速不脏的示例代码。我把它留给你把它构建成一个有用的功能。

它将对空的list 进行除法检查。而且,如果您有非常大的数字或非常长的列表,那么计算 stdev 时的减法可能会丢失精度并返回一个无用的数字。

但它适用于大多数应用程序。

【讨论】:

  • OP 也需要中位数,为此需要对列表进行排序以在 O(n)
  • 哦,你说得对,我错过了中位数要求。正如您所提到的,除非列表 先验 已知要排序,否则单程 O(n) 就这么多。你也可以使用list.Median() 来获取中位数;它进行了适当的优化。
【解决方案2】:

因为要求中位数而标准差需要平均值,所以在 O(n) 中很难做到。

这是我最好的尝试:

private (double min, double max, double mean, double median, double std) CalculateMetrics(List<double> list)
{
    var mean = list.Average();
    var std = Math.Sqrt(list.Aggregate(0.0, (a, x) => a + (x - mean) * (x - mean)) / list.Count());
    var sorted = list.OrderBy(x => x).ToList();
    var median = sorted.Count % 2 == 0 ? (sorted[sorted.Count / 2 - 1] + sorted[sorted.Count / 2]) / 2 : sorted[sorted.Count / 2];
    return (sorted.First(), sorted.Last(), mean, median, std);
}

【讨论】:

    【解决方案3】:

    O(2n) 解:

            private static (double, double, double, double, double) CalculateMetrics(double[] list)
            {
                if (list.Length < 1)
                {
                    throw new Exception();
                }
    
                double min = list[0];
                double max = list[0];
                double median = list[list.Length / 2];
    
                double sum = 0;
                foreach (double el in list)
                {
                    if (el > max)
                    {
                        max = el;
                    }
                    if (el < min)
                    {
                        min = el;
                    }
    
                    sum += el;
                }
    
                double mean = sum / list.Length;
    
                double sumStd = 0;
                foreach (var el in list)
                {
                    sumStd += Math.Pow(el - mean, 2) / list.Length;
                }
                double stdDev = Math.Sqrt(sumStd);
    
                return (min, max, mean, median, stdDev);
            }
    

    【讨论】:

    • 这里没有中位数计算(列表必须排序)
    • 除了需要排序列表外,如果列表长度相等,中位数定义为中间两个的平均值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 2016-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多