以下是Al Kelley / Ira Pohl撰写的 A Book On C(第3版)中的第60-61页的问题:
以下代码片段显示了计算运行平均值的两种不同方法:
int i;
double x;
double avg= sum= 0.0;
double navg;
for (i=1; scanf("%lf", &x)==1; ++i)
{
avg+= (x-avg)/i;
sum+= x;
navg= sum/i;
}
书中写的原始问题是:如果输入一些“普通”数字,平均值和导航似乎是相同的。通过实验证明平均值更好,即使总和没有溢出。
我作为初级程序员的问题是:
“更好”算法的标准是什么?我认为精确度和运行时间是两个关键因素,但还有其他一些能让算法“更好”的东西吗?
在精度和运行时间方面,我如何通过实验证明,当溢出被排除时,avg仍然是比navg更好的方法?我应该使用“与众不同”的数字,例如大小不同的数字吗?
答案 0 :(得分:1)
(1)运行时间: 以下两段代码表明,在1000000的幅度下,两种算法没有太大区别。
#include<stdio.h>
#include<time.h>
int main()
{
int i ;
double x ,sum = 0,avg = 0;
srand(time(NULL));
for(i = 0; i < 1000000 ; i++)
{
x = rand()%10+1;
sum += x;
}
avg = sum/i;
printf("%lf\n",avg);
printf("time use:%lf\n",(double)clock()/CLOCKS_PER_SEC);
}
#include<stdio.h>
#include<time.h>
int main()
{
double sum = 0,avg = 0;
double x;
int i;
srand(time(NULL));
for(i = 0 ; i < 1000000; i++)
{
x = rand()%10+1;
avg += (x-avg)/(i+1);
}
printf("%lf\n",avg);
printf("time use:%lf\n",(double)clock()/CLOCKS_PER_SEC);
}
(2)精度: 下面的代码表明,添加avg和每个x之间的差异,结果为0;而对于navg,结果是-2.44718e-005,这意味着avg在精度上更好。
#include <stdlib.h>
#include <stdio.h>
int main()
{
static double data[1000000];
double sum, avg, check_value;
int i;
int n = sizeof(data)/sizeof(data[0]);
avg = 0;
for( i = 0; i < n; ++ i)
{
avg += ( data[i] - avg) / (i + 1);
}
check_value = 0;
for( i = 0; i < n; ++ i)
{
check_value = check_value + ( data[i] - avg );
}
printf("\navg += (x[i] - avb) / i:\tavg = %g\t check_value = %g", avg, check_value );
for( i = 0; i < n; ++ i )
{
data[i] = 1.3;
}
sum = 0;
for( i = 0; i < n; ++ i)
{
sum += data[i];
}
avg = sum / n;
check_value = 0;
for( i = 0; i < n; ++ i)
{
check_value = check_value + ( data[i] - avg );
}
printf("\n avg = sum / N: \tavg = %g\t check_value = %g", avg, check_value );
getchar();
}
答案 1 :(得分:0)
请注意,即使你执行++ i
,你也会在for()循环中除以零答案 2 :(得分:0)
我认为这是一个有效的问题,尽管措辞不太好。一个问题是,即使the question提到的furins也没有得到很好的表达,并且在收到一个好答案之前就已经关闭了。
然而问题本身是有趣的,特别是对于封闭的一个,它表明它甚至被包含在一本书中,因此它可以引导更多的人朝着一个或另一个方向。
我认为两种算法都不是特别好。在天真的平均值中,看起来我们将失去精确度,或者当平均具有多个差异的数字时我们甚至会丢失数字,但是也可能用其他算法发现相同的结果,可能只是使用不同的输入数据集。
所以,特别是因为它来自现有的书,我认为这是一个非常有效的问题,寻求一些体面的答案。
我试图通过一个例子来掩盖我对这两种算法的看法。所以想象一下你有4个大小相同的数字,你想平均它们。
天真的方法首先将它们一个接一个地总结。在对前两个求和之后,你显然在低端失去了一点精度(因为你现在可能有一个更大的指数)。当您添加最后一个数字时,您有2位丢失(现在使用哪些位来表示总和的高位)。但是你除以4,在这种情况下基本上只是从你的指数中减去2。
在此过程中我们失去了什么?现在,如果所有数字首先被截断2位,则更容易回答。这种情况显然是得到的平均值的最后两位将为零,并且可能引入额外的2位错误(如果所有截断的位都恰好是原始数字中的那些比较,如果它们是零)。所以基本上如果源是具有23位分数的单精度浮点数,那么得到的平均值将具有大约19位的精度。
天真方法的实际结果更好,但总和的第一个数字并没有那么精确。
在每次迭代的差分方法中,将适当加权的差值加到总和中。如果数字具有相同的幅度,那么这种差异很可能会低于一个数量级。然后将其除以当前计数,在此操作中没有任何损失,但是最后一个数字的结果差异(在该示例中i = 4)可能比源数量低约3个数量级。我们将其添加到与原始数字大小相同的运行平均值。
因此,使用此示例中的差分方法添加最后一个数字似乎已经丢失了大约3位的精度,对于所有4个数字,它甚至可能看起来像我们可能会降低到5个基本上丢失的精度 - 可能更糟糕比天真的方法?
差分方法更难以遵循,也许我在我的假设中做了一些错误。但我认为很清楚:看待一个或另一个表现更好似乎没有效果,或者如果是这样,可能取决于数据的布局和幅度差异。