[解決済み] 最も正確な結果を得るためには、どのような順序でフロートを追加すればよいのでしょうか?
質問
先日の面接で聞かれた質問です。 (実は数値解析の理論を覚えていないので、よろしくお願いします :)
浮動小数点数を積算する関数があったとします。
std::accumulate(v.begin(), v.end(), 0.0);
v
は
std::vector<float>
のように、例えば
-
これらの数字を累積する前にソートした方が良いでしょうか?
-
どの順番が最も正確な答えになるでしょうか?
私は、次のように考えています。 ソート を昇順に並べると、実際には数値の誤差が生じます。 が少なくなる。 ということになりますが、残念ながら私自身はそれを証明することができません。
追伸:これはおそらく現実のプログラミングとは何の関係もないことは承知しています。
どのように解決するのですか?
あなたの直感は基本的に正しく、(大きさの)昇順でソートすると、通常は多少改善されます。単精度 (32 ビット) 浮動小数点を加算する場合を考えてみましょう。1 / (10 億) に等しい値が 10 億個あり、1 に等しい値が 1 つあります。1 が最初に来た場合、精度の損失により 1 + (1 / 10 億) が 1 になるので、合計は 1 になります。それぞれの足し算は合計にまったく影響を与えません。
小さな値が最初に来る場合、少なくとも合計は何かになりますが、それでも 2^30 個の値があり、2^25 ほど経過すると、それぞれが個別に合計に影響を及ぼさない状況に戻ってしまいます。だから、もっとトリックが必要なんだ。
これは極端な例ですが、一般に、大きさが非常に異なる 2 つの値を加算するよりも、大きさが同じ 2 つの値を加算する方が正確です。数字を並べ替えることで、同じような大きさの値をグループ化し、昇順で加算することで、小さな値にも大きな値の大きさに累積的に到達する「チャンス」を与えることができるのです。
しかし、負の数が含まれる場合、この方法を裏切るのは簡単です。合計する 3 つの値を考えてみましょう。
{1, -1, 1 billionth}
. 算術的に正しい和は
1 billionth
となりますが、最初の足し算に小さな値が含まれていると、最終的な和は 0 になります。 6 つの可能な順序のうち、2 つだけが "正しい順序です -。
{1, -1, 1 billionth}
と
{-1, 1, 1 billionth}
. 6つのオーダーはすべて、入力の中で最も大きな値のスケールで正確な結果(0.0000001% out)を与えますが、そのうちの4つは真の解のスケールで不正確な結果(100% out)です。前者が十分であるかどうかは、あなたが解決しようとしている特定の問題によってわかります。
実際には、ただソートして足すだけでなく、もっとたくさんのトリックを行うことができます。非常に小さな値がたくさんあり、中間の値があり、大きな値が少ない場合、まず小さな値をすべて合計し、次に中間の値を別々に合計し、その 2 つの合計を合わせてから大きな値を追加するのが最も正確な方法かもしれません。浮動小数点数の足し算の最も正確な組み合わせを見つけるのは簡単ではありませんが、本当に悪いケースに対処するために、異なる大きさの実行合計の配列をすべて保持しておき、新しい値をその大きさに最も合う合計に加えます。実行合計がその大きさに対して大きくなり始めたら、それを次の合計に加え、新しいものを始めることができます。論理的に極端に言えば、この処理は任意精度の型で合計を実行することと同じです(だから、そうするのでしょう)。しかし、大きさの昇順または降順で追加するという単純な選択を考えると、昇順のほうが有利です。
なぜなら、それぞれが個別に合計に影響を与えるには小さすぎる多数の値からなる「重い」尾部を誤って切り落としたり、個別に合計の最後の数ビットにしか影響しない多くの小さな値の精度を捨てたりすると、計算が非常に悪くなるケースがいくつかあるからです。末尾が無視できるようなケースでは、おそらく気にする必要はないでしょう。例えば、最初の段階で少数の値しか加算しておらず、合計の有効数字数個しか使っていない場合などです。
関連
-
[解決済み】Cygwin Make bash コマンドが見つかりません。
-
[解決済み】文字列関数で'char const*'のインスタンスを投げた後に呼び出されるterminate [閉店].
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】エラー。switchステートメントでcaseラベルにジャンプする
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み] std::vectorのイテレータのインデックスを取得する最も効果的な方法は何ですか?
-
[解決済み】sumの順番を変えると違う結果になるのはなぜですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】getline()が何らかの入力の後に使用されると動作しない 【重複あり
-
[解決済み] [Solved] Error C1083: Cannot open include file: 'stdafx.h'
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み] error: 'if' の前に unqualified-id を期待した。
-
[解決済み】C++プログラムでのコンソールの一時停止
-
[解決済み】Visual Studioのデバッガーエラー。プログラムを開始できません 指定されたファイルが見つかりません
-
[解決済み】演算子のオーバーロード C++; <<操作のパラメータが多すぎる
-
[解決済み] 警告:暗黙の定数変換でのオーバーフロー
-
[解決済み】c++で.txtファイルから2次元の配列に読み込む
-
[解決済み】sumの順番を変えると違う結果になるのはなぜですか?