计算所有值的总和超过双倍限制的平均值有什么好的解决方案?

Sim*_*mon 40 java algorithm statistics

我需要计算一组非常大的双精度(10 ^ 9值)的平均值.这些值的总和超过了double的上限,那么是否有人知道用于计算平均值的任何巧妙的小技巧,也不需要计算总和?

我使用的是Java 1.5.

mar*_*nus 167

您可以迭代计算平均值.这个算法简单,快速,你只需要处理每个值一次,变量永远不会大于集合中的最大值,所以你不会得到溢出.

double mean(double[] ary) {
  double avg = 0;
  int t = 1;
  for (double x : ary) {
    avg += (x - avg) / t;
    ++t;
  }
  return avg;
}
Run Code Online (Sandbox Code Playgroud)

循环内部avg始终是到目前为止处理的所有值的平均值.换句话说,如果所有值都是有限的,则不应该溢出.

  • @Martin B:这种方法在数值上是稳定的,并在Knuth,The Art of Computer Programming Vol 2,4.2.2节中推荐.这是迄今为止唯一明智的答案,所以请upvote !!! (20认同)
  • 这应该是公认的答案. (4认同)
  • 小心使用此功能.如果启动期间的数据太大且太小,可能会出现溢出.例如,如果数组是[Double.MIN,Double.MAX],该怎么办?你最终会溢出,因为第一轮和第二轮的分隔线太小了.让我们通过定义MIN = -127和MAX = 128,First Round :: avg + =(-127-0)/ 1 = -127)来证明.然后第二轮avg + =(128 - ( - 127))/ 2 // Boom溢出因为128 - ( - 127)> MAX (2认同)

Dav*_*ide 12

恕我直言,解决你的问题最强大的方法是

  1. 你的集合
  2. 分成一组元素,其总和不会溢出 - 因为它们被排序,这是快速和容易的
  3. 在每组中进行总和 - 并除以组的大小
  4. 做组的和的总和(可能递归地调用相同的算法) - 请注意,如果组的大小不同,你将不得不按它们的大小加权

这种方法的一个好处是它可以很好地扩展,如果你有很多元素可以求和 - 并且有大量的处理器/机器用来做数学

  • @Will:在数学中,总和不依赖于项目的顺序.在浮动指向算术中,确实如此.解决总和问题的最有力的方法,确实是我写的那个:在块中排序和求和.它不是最快的,但它是安全的,并且易于并行化. (4认同)
  • 这种方法也可以通过多线程来制定系统中的所有CPU. (2认同)
  • 如果可以,您希望避免对大量项目进行排序.平均值不依赖于项目的顺序,因此这是一项额外的工作.扫描最大的N个元素将为您提供足够的信息,以便我认为可以选择明智的组大小. (2认同)
  • 糟糕的是,对大型阵列进行排序非常耗时.在迭代它们时,您最好将值分为大小值.看我的解决方案. (2认同)

ang*_*son 12

我想问你的第一个问题是:

  • 你事先知道价值的数量吗?

如果没有,那么你别无选择,只能求和,计算,除以平均值.如果Double没有足够高的精度来处理这个,那么运气不好,你不能使用Double,你需要找到一个可以处理它的数据类型.

如果,另一方面,你知道值的数量事先,你可以看看你真的在做什么和改变如何你做到这一点,但保持了整体效果.

存储在某个集合A中的N值的平均值是:

A[0]   A[1]   A[2]   A[3]          A[N-1]   A[N]
---- + ---- + ---- + ---- + .... + ------ + ----
 N      N      N      N               N       N
Run Code Online (Sandbox Code Playgroud)

要计算此结果的子集,可以将计算拆分为大小相等的集合,这样就可以对3值集合执行此操作(假设值的数量可以除以3,否则需要使用不同的除数)

/ A[0]   A[1]   A[2] \   / A[3]   A[4]   A[5] \   //      A[N-1]   A[N] \
| ---- + ---- + ---- |   | ---- + ---- + ---- |   \\    + ------ + ---- |
\  3      3      3   /   \  3      3      3   /   //        3       3   /
 --------------------- +  --------------------  + \\      --------------
          N                        N                        N
         ---                      ---                      ---
          3                        3                        3
Run Code Online (Sandbox Code Playgroud)

请注意,您需要具有相同大小的集合,否则最后一组中的数字与之前的所有集合相比没有足够的值,将对最终结果产生更大的影响.

按顺序考虑数字1-7,如果你选择3的设置大小,你将得到这个结果:

/ 1   2   3 \   / 4   5   6 \   / 7 \ 
| - + - + - | + | - + - + - | + | - |
\ 3   3   3 /   \ 3   3   3 /   \ 3 /
 -----------     -----------     ---
      y               y           y
Run Code Online (Sandbox Code Playgroud)

这使:

     2   5   7/3
     - + - + ---
     y   y    y
Run Code Online (Sandbox Code Playgroud)

如果所有集合的y为3,则得到以下结果:

     2   5   7/3
     - + - + ---
     3   3    3
Run Code Online (Sandbox Code Playgroud)

这使:

2*3   5*3    7
--- + --- + ---
 9     9     9
Run Code Online (Sandbox Code Playgroud)

这是:

6   15   7
- + -- + -
9    9   9
Run Code Online (Sandbox Code Playgroud)

总计:

28
-- ~ 3,1111111111111111111111.........1111111.........
 9
Run Code Online (Sandbox Code Playgroud)

1-7的平均值是4.显然这不起作用.请注意,如果您使用数字1,2,3,4,5,6,7,0,0执行上述练习(请注意那里的两个零),那么您将获得上述结果.

换句话说,如果你不能将值的数量分成相同大小的集合,那么最后一个集合将被计为好像它具有与它之前的所有集合相同的值的数量,但它将用零填充为零所有缺失的值.

所以,你需要同样大小的套装.如果您的原始输入集包含素数值,那就太好了.

我在这里担心的是失去精确度.Double如果它最初不能保持整个值的总和,我不完全确定在这种情况下会给你足够的精度.

  • 如果你适当地加权,你可以平凡地拥有不同大小的套装. (8认同)

Boz*_*zho 11

除了使用已建议的更好方法之外,您还可以使用BigDecimal进行计算.(请记住它是不可变的)

  • 除非绝对必要,否则不要让生活变得更加困难 - 如果你需要处理非常大的数字或高精度而你可以牺牲时间,那么使用复杂的数字类型是一个很好的方法. (3认同)
  • 较慢是相对术语.在计算10 ^ 9值的平均值的情况下,使用BigDecimal,slow是几分钟(甚至可能是30)...如果需要更快的算法,BigDecimal方法将很好地验证更快的实现. (2认同)
  • 不好的想法,这将创建10 ^ 9个对象.由于所有输入数字都适合双倍范围,因此平均值也适合双倍范围,因此只能使用双精度解决方案. (2认同)

Aln*_*tak 10

请澄清值的潜在范围.

假设double具有范围〜= +/- 10 ^ 308,并且您将10 ^ 9值相加,则您的问题中建议的表观范围是10 ^ 299的数量级.

这似乎有点,不太可能......

如果你的价值观真的那么大,然后用正常的两倍你只拿到了17显著十进制数字一起玩,所以你会扔掉约280位值得的信息之前,你甚至可以考虑一下平均的值.

对于任何一组数字,我也会注意到(因为没有其他人有)X:

mean(X) = sum(X[i] - c)  +  c
          -------------
                N
Run Code Online (Sandbox Code Playgroud)

任何常数c.

在这个特定问题中,设置c = min(X) 可能会大大降低求和期间溢出的风险.

我谦卑地建议问题陈述不完整......?


Dav*_*d M 6

您可以获取不超过限制的相等大小的数字子集的平均值.


Alo*_*lon 6

将所有值除以设定的大小,然后将其求和

  • 但是,你可能会遇到下溢,而不是溢出 (3认同)
  • 这涉及到比必要的更多的部门. (2认同)

Joh*_*ler 6

双精度可以除以2的幂而不会损失精度.因此,如果您唯一的问题是,如果总和的绝对大小,您可以在汇总它们之前预先缩放您的数字.但是对于这样大小的数据集,仍然存在这样的风险:您将遇到向较大的数字添加小数字的情况,并且小数字最终将被大部分(或完全)忽略.

例如,当你添加2.2e-20到9.0e20时,结果是9.0e20,因为一旦调整了比例以便它们的数字可以加在一起,较小的数字就是0.双打只能容纳大约17位,你会需要超过40位才能将这两个数字加在一起而不会丢失.

因此,根据您的数据集和您可以承受的精度数字,您可能需要做其他事情.将数据分成集将有所帮助,但保持精度的更好方法可能是确定粗略平均值(您可能已经知道这个数字).然后在求和之前从粗略平均值中减去每个值.这样你就可以将距离与平均距离相加,所以你的总和永远不会变得非常大.

然后取平均增量,并将其加到粗略的总和中,得到正确的平均值.跟踪最小和最大增量也将告诉您在求和过程中丢失了多少精度.如果你有很多时间并且需要非常准确的结果,你可以迭代.

  • 要总结大小值,应该使用Kahan求和. (2认同)

Ano*_*on. 5

选项1是使用任意精度库,因此您没有上限.

其他选项(失去精确度)是按组而不是一次加总,或在求和之前除.