什么实现平均是最准确的

What implementation of average is the most accurate?

本文关键字:实现 什么      更新时间:2023-10-16

给定这两个平均函数的实现:

float average(const vector<float>& seq)
{
  float sum = 0.0f;
  for (auto&& value : seq)
  {
    sum += value;
  }
  return sum / seq.size();
}

:

float average(const vector<float>& seq)
{
  float avg = 0.0f;
  for (auto&& value : seq)
  {
    avg += value / seq.size();
  }
  return avg;
}

为了说明我的问题,想象我们在输入数据中有一个巨大的差异,就像这样:

1.0f, 0.0f, 0.0f, 0.0f, 1000000.0f

我的猜测是,在第一个实现中,sum可能增长"太多"并丢失最低有效数字,并且在sum循环结束时成为1000000.0f而不是1000001.0f

另一方面,第二个实现在理论上似乎效率较低,因为要执行的除法的数量(我没有分析任何东西,这是一个盲目的猜测)。

那么,这些实现中的一个比另一个更好吗?我是不是说第一个实现不太准确?

我不指望第二个更准确。的元素大小的差异除以向量的长度,但每次除法都会引入一些额外的不精确。

如果精度有问题,应该使用第一步double。即使向量是float,出于内存原因,函数内的计算应该是double .

除此之外,对于大量元素,您可能应该使用Kahan算法,而不仅仅是naïvely添加元素。虽然它在循环中增加了一些操作,它跟踪错误,并将导致显著更多的准确性。

编辑:

只是为了好玩,我写了一个小程序,使用下面的代码生成向量:

std::vector<float> v;
v.push_back( 10000000.0f );
for ( int count = 10000000; count > 0; -- count ) {
    v.push_back( 0.1f );
}

平均结果应为1.0999999(实际来说,1.1)。使用原始算法中的任意一种发布后,结果为0.999999881:误差为10%。只是在第一个算法中将sum更改为double类型,然而,1.0999999的结果,几乎是你能做到的最准确的得到的。使用Kahan算法(到处都是float)给出

如果您的总和对于类型float不是太大,则第一个可能更准确,因为除法产生的单个四舍五入错误可能会累积