计算大数的加权平均值

我想要得到几个数字的加权平均值。基本上我有:
Price    - 134.42
Quantity - 15236545
可以有少至一个或两个或多达五十或六十对价格和数量。我需要弄清楚价格的加权平均值。基本上,加权平均值应该给对象提供非常小的权重
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;
问题是我很快就将“滚动”变量最大化了。 我怎样才能真正得到加权平均值?     
已邀请:
一个double可以容纳一个相当大的数字(根据文档大约1.7 x 10 ^ 308),但你可能不应该将它用于需要精确精度的值(例如货币值)。 请查看BigDecimal类。关于SO的这个问题更详细地讨论了它。     
一种解决方案是对
rolling
totalQuantity
使用
java.math.BigInteger
,并且只在末尾划分它们。这具有更好的数值稳定性,因为最后只有一个浮点除法,其他所有都是整数运算。
BigInteger
基本上是无界限的,所以你不应该遇到任何溢出。 编辑:对不起,只有在重新阅读时我才注意到你的价格是
double
。也许值得通过将它乘以100然后转换为ѭ7来规避这一点 - 因为我在你的例子中看到它在小数点右边正好有两位数 - 然后在结尾处除以100,尽管这有点像黑客攻击。     
为获得最大的灵活性,使用
BigDecimal
表示
rolling
,使用
BigInteger
表示
totalQuantity
。划分后(注意,你将它向后移动;它应该是滚动/ totalQuantity),你可以返回一个BigDecimal,或者在精度不足的情况下使用
doubleValue
。     
在任何给定点,您都记录了总值
ax + by + cz + ... = pq
和总重量
a + b + c + ... = p
。知道两者然后给你平均值
pq/p = q
。问题是
pq
p
是大量的溢出,即使你只是想要适度大小的
q
。 例如,下一步增加了
r
的重量和
s
的值。你想通过仅使用
q
的值找到新的和ѭ23,只有当
p
pq
以某种方式“湮灭”时才会出现在相同分数的分子和分母中。这是不可能的,正如我将展示的那样。 此迭代中需要添加的值自然是
(pq + rs) / (p + r) - q
这不能简化到
p*q
p
消失的程度。你也可以找到
(pq + rs) / q(p + r)
你为了得到下一个平均值而乘以q的因素;但是,
pq
p
仍然存在。所以没有聪明的解决方案。 其他人提到了任意精度变量,这是一个很好的解决方案。
p
pq
的大小随着条目的数量线性增长,整数/浮点数的内存使用和计算速度随着值的大小呈对数增长。所以性能是O(log(n)),不像灾难,如果
p
不知何故是多个数字的倍数。     
首先,我不知道你怎么能“最大化”
rolling
变量。正如@Ash指出的那样,它可以代表高达约37ѭ的值。我能想到的唯一可能性就是你的输入中有一些不好的值。 (也许真正的问题是你正在失去精确度......) 其次,你使用
Map
来表示订单是奇怪的,可能会被打破。您目前使用它的方式,您不能代表涉及两个或更多具有相同价格的物品的订单。     
你的最终结果只是精确度的加权平均值,所以你可能不需要遵循计算账户余额时使用的规则等。如果我对上述内容是正确的,那么你不需要使用
BigDecimal
double
将满足。 溢出问题可以通过存储“运行平均值”并用每个新条目更新它来解决。即,让 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,另一个是10000000),那么我不是百分百肯定你是否会得到令人满意的准确性,但你可以在一些例子中测试它,当你知道答案应该是什么时。     
做两个循环:在第一个循环中首先计算totalQuantity。然后在第二个循环中累积价格*(数量/ totalQuantity)。     

要回复问题请先登录注册