3

float の配列の平均を計算しようとしています。これはバイナリ検索内にあるため、インデックスを使用する必要があり、上部と下部が移動します。(半分の範囲の推定を最適化しようとしている全体像なので、パスごとに配列を再作成する必要はありません)。

とにかく、私はカスタム平均ループを書きましたが、c# Average() メソッドよりも精度が 2 桁低くなります。

float test = input.Average();

int count = (top - bottom) + 1;//number of elements in this iteration
int pos = bottom;
float average = 0f;//working average
while (pos <= top)
{
     average += input[pos];
     pos++;
}
average = average / count;

例:

0.0371166766 - c#
0.03711666 - 私のループ

125090.148 - c#
125090.281 - 私のループ

http://pastebin.com/qRE3VrCt

4

3 に答える 3

5

私はc#Average()よりも2桁低い精度を得ています

いいえ、有効数字は1桁しか失われていません。フロートタイプは有効数字7桁しか格納できず、残りは単なるランダムノイズです。必然的に、このような計算では、丸め誤差が累積され、精度が低下する可能性があります。丸め誤差を相殺するためには運が必要です。

これを回避する唯一の方法は、結果を累積するためにより正確な浮動小数点型を使用することです。問題ありません、あなたは2倍利用可能です。これが、LinqAverageメソッドが次のようになっている理由です。

   public static float Average(this IEnumerable<float> source) {
       if (source == null) throw Error.ArgumentNull("source");
       double sum = 0;         // <=== NOTE: double
       long count = 0;
       checked {
           foreach (float v in source) {
               sum += v;
               count++;
           }
       }
       if (count > 0) return (float)(sum / count);
       throw Error.NoElements();
   }

doubleを使用して、結果に有効桁数が同程度のLinq結果を再現します。

于 2013-02-12T18:57:14.037 に答える
2

これを次のように書き直します。

int count = (top - bottom) + 1;//number of elements in this iteration
double sum = 0;
for(int i = bottom; i <= top; i++)
{
     sum += input[i];
}
float average = (float)(sum/count);

そうすれば、丸め誤差を減らすのに役立つ高精度アキュムレータを使用できます。

ところで。パフォーマンスがそれほど重要でない場合でも、LINQ を使用して配列スライスの平均を計算できます。

input.Skip(bottom).Take(top - bottom + 1).Average()

それがあなたの問題に当てはまるかどうかは完全にはわかりませんが、多くのサブ配列の平均を計算する必要がある場合は、永続的な合計配列を作成すると便利な場合があるため、平均の計算は単純に 2 つのテーブル ルックアップと除算になります。

于 2013-02-12T18:39:02.440 に答える