2014-09-28 91 views
1

什么是计算组号码,ARR[0]/N+ARR[1]/N...+ARR[N-1]/N(ARR[0]+ARR[1]...+ARR[N-1])/N的平均更准确的方法是什么? (ARR是一组数字和N是数字的那一套计)什么是更准确的平均方法,ARR [0]/N + ARR [1]/N ... + ARR [N-1]/N或(ARR [0] + ARR [1] ... + ARR [N-1])/ N为双倍?

考虑我已经设置从0.01.0每个范围(它们是双\浮点数)号和有几千其中甚至数百万。

我公开像递归平均新方法(平均双细胞分化成数组,然后再次平均,直到它输出一个单元阵列)。

+0

(假设所有数字都是正数)对数字进行排序,从最低到最高,然后从最低到最高添加。 (如果出现负数,则按绝对值排序。)并且不需要将每个元素除以N,只需将总和除以N. – 2014-09-28 18:52:33

+0

另请参阅http://stackoverflow.com/q/13417670/(特别是,Kahan求和) – Nemo 2014-09-28 19:01:02

回答

2

如果接近零值非常接近零,你就会有一个四舍五入的问题的总和(可以舍入误差向上或向下),或者如果总结大组数字的任何数值范围。解决此问题的一个方法是使用求和函数,该求和函数仅添加具有相同指数的数字(直到您调用getsum()以获取总和,并使指数尽可能接近)。示例C++类来执行此操作(注意代码是使用Visual Studio编译的,在uint64_t可用之前编写)。

// SUM contains an array of 2048 IEEE 754 doubles, indexed by exponent, 
// used to minimize rounding/truncation issues when doing 
// a large number of summations 

class SUM{ 
    double asum[2048]; 
public: 
    SUM(){for(int i = 0; i < 2048; i++)asum[i] = 0.;} 
    void clear(){for(int i = 0; i < 2048; i++)asum[i] = 0.;} 
// getsum returns the current sum of the array 
    double getsum(){double d = 0.; for(int i = 0; i < 2048; i++)d += asum[i]; 
        return(d);} 
    void addnum(double); 
}; 

void SUM::addnum(double d)  // add a number into the array 
{ 
size_t i; 

    while(1){ 
//  i = exponent of d 
     i = ((size_t)((*(unsigned long long *)&d)>>52))&0x7ff; 
     if(i == 0x7ff){   // max exponent, could be overflow 
      asum[i] += d; 
      return; 
     } 
     if(asum[i] == 0.){  // if empty slot store d 
      asum[i] = d; 
      return; 
     } 
     d += asum[i];   // else add slot to d, clear slot 
     asum[i] = 0.;   // and continue until empty slot 
    } 
} 
使用之类

示例程序:

#include <iostream> 
#include <iomanip> 
using namespace std; 

static SUM sum; 

int main() 
{ 
double dsum = 0.; 
double d = 1./5.; 
unsigned long i; 

    for(i = 0; i < 0xffffffffUL; i++){ 
     sum.addnum(d); 
     dsum += d; 
    } 
    cout << "dsum    = " << setprecision(16) << dsum << endl; 
    cout << "sum.getsum()  = " << setprecision(16) << sum.getsum() << endl; 
    cout << "0xffffffff * 1/5 = " << setprecision(16) << d * (double)0xffffffffUL << endl; 

    return(0); 
} 
+0

目前还不清楚这是否可能是添加一堆数字的最佳方式。你知道这是真的吗?如果是这样,你能提供一个简单的证明简图吗? – thang 2014-09-28 18:37:10

+0

@thang:这不是最好的方法。但它是我能想到的最合理的方式。对数字进行求和以消除小数量截断的最低误差方式是对数字进行排序并将它们从小到大进行求和。 – 2014-09-28 18:42:20

+0

@RafaelBaptista - 总结一组排序的数字并不能解决问题。你仍然可能会用相同的指数对大量数字求和。在这个序列的结尾附近,您将为相对较大的工作总和添加一个相对较小的数字。 – rcgldr 2014-09-28 18:53:13

0

(ARR[0]+ARR[1]...+ARR[N-1])/N更快,更准确,因为您省略了无用的部门N,这既减慢了过程速度,又增加了计算中的错误。

+0

确保您使用的是一些好的求和算法,如[这里](http://en.wikipedia.org/wiki/Kahan_summation_algorithm) – 2014-09-28 18:30:25

+0

为什么划分为每个值计算中正在错误?我认为当你添加两个值,例如'10000'和'0.1'时,由于它非常小,所以'0.1'轮的误差非常大。我需要这个可靠的来源。 – KugBuBu 2014-09-28 18:33:53

+0

作为提示,在添加它们之前按数量级分类,并从低端开始。 (无论如何,如果数字可以正确无误地添加没有关系) – Deduplicator 2014-09-28 18:36:43

0

如果你有一堆浮点数的,最准确的方式来获得平均是这样的:

template<class T> T mean(T* arr, size_t N) { 
    std::sort(+arr, arr+N, [](T a, T b){return std::abs(a) < std::abs(b);}); 
    T r = 0; 
    for(size_t n = 0; n < N; n++) 
     r += arr[n]; 
    return r/N; 
} 

重要事项:

  • 数字o f首先添加最小幅度以保留有效数字。
  • 只有一个部门,以减少那里的舍入误差。

尽管如此,中间和值可能会变得过于庞大。

相关问题