6

いくつかの数値の加重平均を取得しようとしています。基本的に私は持っています:

Price    - 134.42
Quantity - 15236545

価格と数量のペアは、1 つか 2 つ、あるいは 50 から 60 もの場合があります。価格の加重平均を計算する必要があります。基本的に、加重平均は次のようなペアにほとんど重みを与えません。

Price    - 100000000.00
Quantity - 3

上記のペアにさらに。

私が現在持っている式は次のとおりです。

((price)(quantity) + (price)(quantity) + ...)/totalQuantity

これまでのところ、私はこれを行っています:

        double optimalPrice = 0;
        int totalQuantity = 0;
        double rolling = 0;
        System.out.println(rolling);

        Iterator it = orders.entrySet().iterator();
        while(it.hasNext()) {
            System.out.println("inside");
            Map.Entry order = (Map.Entry)it.next();
            double price = (Double)order.getKey();
            int quantity = (Integer)order.getValue();
            System.out.println(price + " " + quantity);

            rolling += price * quantity;
            totalQuantity += quantity;
            System.out.println(rolling);
        }
        System.out.println(rolling);
        return rolling/totalQuantity;

問題は、「ローリング」変数を非常に迅速に最大化することです。

実際に加重平均を取得するにはどうすればよいですか?

4

7 に答える 7

3

java.math.BigInteger1 つの解決策は、 との両方rollingに使用totalQuantityし、最後にのみ分割することです。最後に浮動小数点除算が 1 つしかなく、それ以外はすべて整数演算であるため、これにより数値の安定性が向上します。

BigInteger基本的に無制限であるため、オーバーフローに遭遇することはありません。

編集:申し訳ありませんが、再読したときにのみ、あなたの価格がdoubleとにかくであることに気付きました. たぶん、これを100で掛けてから変換することでこれを回避する価値がありBigIntegerます.

于 2010-05-30T07:07:38.363 に答える
3

double はかなり大きな数 (ドキュメントによると約 1.7 x 10^308) を保持できますが、正確な精度が必要な値 (金銭的な値など) には使用しないでください。

代わりにBigDecimalクラスを確認してください。SOに関するこの質問では、それについて詳しく説明しています。

于 2010-05-30T07:09:24.373 に答える
1

最大限の柔軟性を得るには、 BigDecimalforrollingおよびBigIntegerfor を使用しtotalQuantityます。除算後 (逆方向にあることに注意してください。ローリング / totalQuantity である必要があります)、BigDecimal を返すか、doubleValue精度を落として使用することができます。

于 2010-05-30T07:14:05.797 に答える
0

任意の時点で、合計値ax + by + cz + ... = pq 合計重量の両方を記録しましたa + b + c + ... = p。両方を知っていると、平均値が得られますpq/p = q。問題は、適度なサイズが必要な場合でも、とがオーバーフローする大きな合計であるということpqです。pq

次のステップでは、たとえば、の重みrと値を追加しますs(pq + rs) / (p + r)の値のみを使用して新しい合計を見つけたいと考えています。これは、同じ分数の分子と分母に存在することによって、何らかの形で「消滅」した場合にqのみ発生する可能性があります。これから説明するように、それは不可能です。ppq

この反復で追加する必要のある値は、当然、

(pq + rs) / (p + r) - q

p*qこれは、単純化してp消えるところまで単純化することはできません。あなたも見つけることができます

(pq + rs) / q(p + r)

次の平均を得るためにqを掛ける係数。しかし、再び、pqそしてp残ります。したがって、賢い解決策はありません。

他の人は任意精度の変数に言及しました、そしてそれはここで良い解決策です。のサイズppqはエントリ数に比例して増加し、整数/浮動小数点数のメモリ使用量と計算速度は値のサイズに応じて対数的に増加します。したがって、パフォーマンスはO(log(n))であり、災害とは異なり、p何とかして多くの数値の倍数である場合とは異なります。

于 2010-05-30T07:44:49.950 に答える
0

rollingまず、変数を「最大化」する方法がわかりません。@Ashが指摘しているように、最大​​で約。までの値を表すことができます1.7 x 10^308。私が考えることができる唯一の可能性は、入力にいくつかの悪い値があるということです。(おそらく本当の問題はあなたが精度を失っているということです...)

第二に、Map注文を表すasの使用は奇妙で、おそらく壊れています。現在の使用方法では、同じ価格の2つ以上のアイテムを含む注文を表すことはできません。

于 2010-05-30T07:46:07.437 に答える
0

最終結果は正確さの加重平均にすぎないため、おそらく口座残高などを計算するときに使用されるルールに従う必要はありません。上記について正しい場合は、を使用する必要はありませBigDecimaldouble.

オーバーフローの問題は、「移動平均」を保存し、新しいエントリごとに更新することで解決できます。つまり、

a_n = (sum_{i=1}^n x_i * w_i) / (sum_{i=1}^n w_i)

n = 1、...、N の場合。a_n = x_n で開始し、次に追加します。

d_n := a_{n+1} - a_n

それに。d_n の式は次のとおりです。

d_n = (x_{n+1} - w_{n+1}*a_n) / W_{n+1}

ここで、W_n := sum_{i=1}^n w_n. W_n を追跡する必要がありますが、この問題は次のように保存することで解決できますdouble(平均だけに関心があるので問題ありません)。すべての重みが 1000 の倍数であることがわかっている場合は、それらを 1000 で割るだけで、重みを正規化することもできます。

追加の精度を得るには、補償された合計を使用できます。

先制的な説明: ここでは浮動小数点演算を使用しても問題ありません。double相対精度は 2E-16 です。OP は正の数を平均しているため、キャンセル エラーは発生しません。任意精度演算の支持者が教えてくれないことは、丸め規則は別として、IEEE754 浮動小数点演算よりも多くの追加精度が得られる場合、これにはかなりのメモリとパフォーマンスのコストがかかるということです。浮動小数点演算は、非常に頭の良い人々 (特に Kahan 教授) によって設計されました。浮動小数点演算が提供する精度よりも安価に演算精度を上げる方法があれば、彼らはそれを実行するでしょう。

免責事項: 重みが完全に狂っている場合 (1 つは 1、もう 1 つは 10000000)、満足のいく精度が得られるかどうかは 100% わかりませんが、答えがどうあるべきかわかっている場合は、いくつかの例でテストできます。

于 2010-05-30T08:46:13.803 に答える
0

2 つのループを実行します。最初のループで最初に totalQuantity を計算します。次に、2 番目のループで価格 * (数量 / 合計数量) を累積します。

于 2010-05-30T11:04:14.377 に答える