直截了当(并且 C# > 6.0),Dynamis 的答案变成了这样:
public static double StdDev(this IEnumerable<double> values)
{
var count = values?.Count() ?? 0;
if (count <= 1) return 0;
var avg = values.Average();
var sum = values.Sum(d => Math.Pow(d - avg, 2));
return Math.Sqrt(sum / count);
}
编辑 2020-08-27:
我带@David Clarke cmets 进行了一些性能测试
结果如下:
public static (double stdDev, double avg) StdDevFast(this List<double> values)
{
var count = values?.Count ?? 0;
if (count <= 1) return (0, 0);
var avg = GetAverage(values);
var sum = GetSumOfSquareDiff(values, avg);
return (Math.Sqrt(sum / count), avg);
}
private static double GetAverage(List<double> values)
{
double sum = 0.0;
for (int i = 0; i < values.Count; i++)
sum += values[i];
return sum / values.Count;
}
private static double GetSumOfSquareDiff(List<double> values, double avg)
{
double sum = 0.0;
for (int i = 0; i < values.Count; i++)
{
var diff = values[i] - avg;
sum += diff * diff;
}
return sum;
}
我用一百万个随机双打列表对此进行了测试
原始实现的运行时间约为 48 毫秒
性能优化实现 2-3ms
所以这是一个显着的改进。
一些有趣的细节:
摆脱 Math.Pow 带来了 33 毫秒的提升!
List 而不是 IEnumerable 6ms
手动平均计算4ms
For-loops 而不是 ForEach-loops 2ms
Array 而不是 List 只带来了约 2% 的改进,所以我跳过了这个
使用 single 而不是 double 什么都没有
进一步降低代码并使用 goto(是的 GOTO...自 90 年代汇编程序以来就没有使用过这个...)而不是 for 循环
不付钱,谢天谢地!
我还测试了并行计算,这对列表 > 200.000 项有意义
似乎硬件和软件需要进行很多初始化,这对于小型列表会适得其反。
所有测试连续执行两次以摆脱预热时间。