有没有办法找到比sum()/ N“更好”的算术平均值?

时间:2009-08-28 12:56:04

标签: algorithm language-agnostic math

假设我们有N个数字(整数,浮点数,你想要的任何东西),并希望找到它们的算术平均值。最简单的方法是对所有值求和并除以值的数量:

def simple_mean(array[N]): # pseudocode
    sum = 0
    for i = 1 to N
       sum += array[i]
    return sum / N

它工作正常,但需要大整数。 如果我们不想要大整数并且我们对舍入误差很好,并且N是2的幂,我们可以使用“分而治之”:((a+b)/2 + (c+d)/2)/2 = (a+b+c+d)/4((a+b+c+d)/4 + (e+f+g+h)/4)/2 = (a+b+c+d+e+f+g+h)/8,依此类推。

def bisection_average(array[N]):
   if N == 1: return array[1]
   return (bisection_average(array[:N/2])+bisection_average(array[N/2:]))/2

还有其他方法吗?

PS。 playground for lazy

6 个答案:

答案 0 :(得分:29)

Knuth列出了以下计算给定浮点的平均值和标准差的方法(1998年版Vol 2 of The Art of Computer Programming第232页上的原始版本;我在下面的修改避免了第一次迭代的特殊套管):

double M=0, S=0;

for (int i = 0; i < N; ++i)
{
    double Mprev = M;
    M += (x[i] - M)/(i+1);
    S += (x[i] - M)*(x[i] - Mprev);
}

// mean = M
// std dev = sqrt(S/N) or sqrt(S/N+1)
// depending on whether you want population or sample std dev

答案 1 :(得分:17)

这是一种使用整数计算均值而无需舍入误差并避免大中间值的方法:

sum = 0
rest = 0
for num in numbers:
  sum += num / N
  rest += num % N
  sum += rest / N
  rest = rest % N

return sum, rest

答案 2 :(得分:3)

如果大整数有问题......可以吗

a/N + b/N+.... n/N

我的意思是你只是寻找其他方式或最佳方式?

答案 3 :(得分:3)

如果数组是浮点数据,即使是“简单”算法也会遇到舍入误差。有趣的是,在这种情况下,将计算阻塞为sqrt(N)长度sqrt(N)的总和实际上减少了平均情况下的误差(即使执行了相同数量的浮点舍入)。

对于整数数据,请注意您不需要一般的“大整数”;如果数组中的元素少于40亿(可能),则只需要比数组数据类型大32位的整数类型。在这个稍微大一点的类型上执行加法几乎总是比在类型本身上进行除法或模数更快。例如,在大多数32位系统上,64位加法比32位除法/模数更快。随着类型变大,这种效果只会变得更加夸张。

答案 4 :(得分:1)

如果你使用float,你可能会避免使用大整数:

def simple_mean(array[N]):
    sum = 0.0 # <---
    for i = 1 to N
       sum += array[i]
    return sum / N

答案 5 :(得分:0)

Kahan algorithm(根据维基百科)具有更好的运行时性能(比成对求和) - O(n) - 以及O(1)错误增长:

function KahanSum(input)
    var sum = 0.0
    var c = 0.0                  // A running compensation for lost low-order bits.
    for i = 1 to input.length do
        var y = input[i] - c     // So far, so good: c is zero.
        var t = sum + y          // Alas, sum is big, y small, so low-order digits of y are lost.
        c = (t - sum) - y // (t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
        sum = t           // Algebraically, c should always be zero. Beware overly-aggressive optimizing compilers!
        // Next time around, the lost low part will be added to y in a fresh attempt.
    return sum

它的想法是浮点数的低位与主求和无关地求和和校正。