从多个数字数组创建平均数组的最快方法

本文关键字:数组 方法 创建 数字 | 更新日期: 2023-09-27 17:59:44

我有多个Float的输入数组,所有数组大小相等,只有一个维度。我想创建一个包含所有输入数组的平均值的单个输出数组。

例如

//input arrays
float[] array1 = new float[] { 1, 1, 1, 1 };
float[] array2 = new float[] { 2, 2, 2, 2 };
float[] array3 = new float[] { 3, 3, 3, 3 };
float[] array4 = new float[] { 4, 4, 4, 4 };
//the output should be
float[2.5, 2.5, 2.5, 2.5]

我还想计算输入数组到的标准偏差

完成这项任务最快的方法是什么?

提前谢谢。Pete

从多个数字数组创建平均数组的最快方法

LINQ前往救援

本文详细介绍了如何使用LINQ来实现这一目标,以最大限度地提高可重用性和多功能性为主要目标。

取1(为了方便起见,将LINQ打包为一种方法)

采用这种方法:

float[] ArrayAverage(params float[][] arrays)
{
    // If you want to check that all arrays are the same size, something
    // like this is convenient:
    // var arrayLength = arrays.Select(a => a.Length).Distinct().Single();
    return Enumerable.Range(0, arrays[0].Length)
               .Select(i => arrays.Select(a => a.Skip(i).First()).Average())
               .ToArray();
}

它通过取范围[0..arrays.Length-1]来工作,并且对于该范围中的每个数字i,它计算每个阵列的第i个元素的平均值。它可以非常方便地使用:

float[] array1 = new float[] { 1, 1, 1, 1 };
float[] array2 = new float[] { 2, 2, 2, 2 };
float[] array3 = new float[] { 3, 3, 3, 3 };
float[] array4 = new float[] { 4, 4, 4, 4 };
var averages = ArrayAverage(array1, array2, array3, array4);

这已经可以在任何数量的阵列上使用,而无需修改。但你可以更进一步,做一些更一般的事情。

取2(对任何聚合函数进行泛化)

float[] ArrayAggregate(Func<IEnumerable<float>, float> aggregate, params float[][] arrays)
{
    return Enumerable.Range(0, arrays[0].Length)
               .Select(i => aggregate(arrays.Select(a => a.Skip(i).First())))
               .ToArray();
}

这可用于计算任何聚合函数:

var output = ArrayAggregate(Enumerable.Average, array1, array2, array3, array4);

代替Enumerable.Average,您可以替换任何方法、扩展方法或匿名函数——这很有用,因为没有内置的标准偏差聚合函数,而且ArrayAggregate函数非常通用。但我们仍然可以做得更好。

Take 3(对任何聚合函数和任何类型的数组进行泛化)

我们还可以制作一个适用于任何内置类型的通用版本:

T[] ArrayAggregate<T>(Func<IEnumerable<T>, T> aggregate, params T[][] arrays)
{
    return Enumerable.Range(0, arrays[0].Length)
               .Select(i => aggregate(arrays.Select(a => a.Skip(i).First())))
               .ToArray();
}

正如您可能知道的,这不是完成这项工作最快的代码。如果你的程序花了一整天的时间计算平均值,请使用更接近金属的东西。然而,如果你想要可重用性和多功能性,我认为你不能做得比以上更好。

性能方面最快的方法是,除非您想展开for循环

float[] sums = new float[4];
for(int i = 0; i < 4; i++)
{
    sums[i] = (array1[i]+ array2[i] + array3[i] + array4[i])/4;
}
  static void Main()
  {
     float[] array1 = new float[] { 1, 1, 1, 1 };
     float[] array2 = new float[] { 2, 2, 2, 2 };
     float[] array3 = new float[] { 3, 3, 3, 3 };
     float[] array4 = new float[] { 4, 4, 4, 4 };  
     float[] avg = CrossAverage (array1, array2, array3, array4);
     Console.WriteLine (string.Join ("|", avg.Select(f => f.ToString ()).ToArray()));
  }
  private static float[] CrossAverage (params float [][] arrays)
  {
     int [] count = new int [arrays[0].Length];
     float [] sum = new float [arrays[0].Length];
     for (int j = 0; j < arrays.Length; j++)
     {
        for (int i = 0; i < count.Length; i++)
        {
           count[i] ++;
           sum[i] += arrays[j][i];
        }
     }
     float [] avg = new float [arrays[0].Length];
     for (int i = 0; i < count.Length; i++)
     {
        avg[i] = sum[i] / count[i];
     }
     return avg;
  }

不要忘记边界检查和除以0检查。

对于计算平均值后的标准偏差(进入总和数组):

// std dev
float[] stddevs = new float[4];
for (int i = 0; i < 4; i++)
{
    stddevs[i] += (array1[i] - sums[i]) * (array1[i] - sums[i]);
    stddevs[i] += (array2[i] - sums[i]) * (array2[i] - sums[i]);
    stddevs[i] += (array3[i] - sums[i]) * (array3[i] - sums[i]);
    stddevs[i] += (array4[i] - sums[i]) * (array4[i] - sums[i]);
}
for (int i = 0; i < 4; i++)
    stddevs[i] = (float)Math.Sqrt(stddevs[i]/4);

通常,由于允许编译器/JIT进行优化,直接访问数组而不是使用LINQ将是一种性能优势。至少,可以消除数组边界检查,并且可以避免使用枚举器的开销。