-1

平均関数のこれら 2 つの実装を考えると、次のようになります。

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「大きくなりすぎて」最下位桁が失われ、1000000.0f代わりに1000001.0f合計ループの最後になる可能性があります。

一方、2 番目の実装は、実行する除算の数が多いため、理論的には効率が悪いようです (私は何もプロファイリングしていません。これは盲目的な推測です)。

それで、これらの実装の1つは他の実装よりも望ましいですか? 最初の実装は精度が低いというのは本当ですか?

4

2 に答える 2

5

2番目がより正確であるとは考えていません。要素のサイズの違いはベクトルの長さで除算されますが、除算ごとにさらに不正確さが生じます。

精度に問題がある場合、最初のステップは を使用すること doubleです。ベクトルがfloatであっても、メモリ上の理由から、関数内の計算は である必要がありますdouble

それを超えて、多数の要素の場合、単純に要素を追加するのではなく、おそらくKahan アルゴリズムを使用する必要があります。ループに多くの操作が追加されますが、エラーを追跡するため、精度が大幅に向上します。

編集:

楽しみのために、次のコードを使用してベクトルを生成する小さなプログラムを作成しました。

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型を持つように変更するだけで、取得できるのとほぼ同じ精度になります。Kahan アルゴリズム (どこでも float) を使用すると、同じ結果が得られます。double1.0999999

于 2013-05-03T08:23:12.733 に答える