Directamente al grano (y C #> 6.0), la respuesta de Dynamis se convierte en esta:
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);
}
Editar 2020-08-27:
Tomé los comentarios de @David Clarke para hacer algunas pruebas de rendimiento y estos son los resultados:
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;
}
Probé esto con una lista de un millón de dobles aleatorios;
la implementación original tenía un tiempo de ejecución de ~ 48 ms;
la implementación optimizada para el rendimiento, 2-3 ms
por lo que esta es una mejora significativa.
Algunos detalles interesantes: ¡
deshacerse de Math.Pow trae un impulso de 33ms!
List en lugar de IEnumerable 6ms
manualmente Cálculo promedio 4ms
For-loops en lugar de ForEach-loops 2ms
Array en lugar de List trae solo una mejora de ~ 2%, así que omití esto
usando single en lugar de double no trae nada
Bajar aún más el código y usar goto (sí, GOTO ... no he usado esto desde el ensamblador de los 90 ...) en lugar de bucles for no paga, ¡Gracias a Dios!
También he probado el cálculo paralelo, esto tiene sentido en la lista> 200.000 elementos. Parece que el hardware y el software necesitan inicializarse mucho y esto es contraproducente para listas pequeñas.
Todas las pruebas se ejecutaron dos veces seguidas para eliminar el tiempo de calentamiento.