如何在一组大数字中找到平均值?

时间:2009-05-21 21:51:37

标签: c# math memory

我有一大堆数字,可能在几千兆字节范围内。第一个问题是我无法将所有这些存储在内存中。其次是任何添加这些的尝试都会导致溢出。我在考虑使用更多的滚动平均值,但它需要准确。有什么想法吗?

这些都是浮点数。

这不是从数据库中读取的,而是从多个来源收集的CSV文件。它必须准确,因为它存储为秒的一部分(例如; 0.293482888929),滚动平均值可以是.2和.3之间的差异

这是一组代表用户响应某些表单操作的时间。例如,在显示消息框时,按“确定”或“取消”需要多长时间。数据发送给我存储为秒。部分秒;例如1.2347秒。将它转换为毫秒,我溢出int,long等等。相当快。即使我不转换它,我仍然会很快溢出它。我想下面的一个答案是正确的,也许我不必100%准确,只要在一个特定的StdDev中的某个范围内查看,我就足够了。

14 个答案:

答案 0 :(得分:18)

您可以从您的集合中随机抽样(“population”)以获得平均值(“mean”)。准确度将取决于样本的变化程度(由“standard deviation”或方差确定)。

优点是你有数十亿的观察结果,你只需要对它们中的一小部分进行采样,以获得不错的准确度或你选择的“confidence range”。如果条件合适,这会减少你将要做的工作量。

这是C#的numerical library,包括随机序列生成器。只需创建一个随机的数字序列,引用元素数组中的索引(从1到 x ,数组中的元素数量)。取消引用以获取值,然后计算您的平均值和标准差。

如果您想测试数据的分布,请考虑使用Chi-Squared Fit测试或K-S测试,您可以在许多电子表格和统计数据包中找到它(例如{{3} })。这将有助于确认这种方法是否可用。

答案 1 :(得分:13)

整数还是花车?

如果它们是整数,则需要通过读取数字并记录您看到的每个值的数量来累积频率分布。这很容易平均化。

对于浮点,这有点问题。鉴于浮动的总体范围和实际分布,您必须计算出一个bin大小,以保留您想要的精度而不保留所有数字。


修改

首先,您需要对数据进行采样以获得均值和标准差。几千点应该足够好了。

然后,您需要确定一个可敬的范围。人们在平均值周围选择±6σ(标准偏差)之类的东西。您可以将此范围划分为尽可能多的铲斗。

实际上,存储桶的数量决定了平均值中的有效位数。因此,选择10,000或100,000个桶来获得4或5位精度。由于这是一种测量,因此您的测量只有两位或三位数的几率很高。


修改

您将发现的是,您的初始样本的平均值非常接近任何其他样本的平均值。任何样本均值接近人口均值。你会注意到你的大多数(但不是全部)手段彼此之间有1个标准差。

您应该发现测量误差和误差大于标准偏差。

这意味着样本均值与总体均值一样有用。

答案 2 :(得分:9)

滚动平均值不会像其他任何东西一样准确(折扣舍入错误,我的意思)?由于所有分裂,它可能有点慢。

您可以对批量数字进行分组并递归地对其进行平均。像平均100个数字100次,然后平均结果。这可能会减少骚扰,而且大部分都会增加。

实际上,如果你一次添加256或512,你可以将结果按8位或9位移位(我相信你可以通过简单地改变浮点尾数来做到这一点) - 这将使你的程序非常快,它可以只用几行代码递归写入(不计算尾数移位的不安全操作)。

也许除以256会使用这种优化?我可能要加速测试除以255对256,看看是否有一些大的改进。我猜不是。

答案 3 :(得分:7)

您的意思是32位和64位数字。但为什么不只使用一个合适的Rational Big Num库?如果你有这么多数据,并且你想要一个精确的平均值,那么只需编码即可。

class RationalBignum {
    public Bignum Numerator { get; set; }
    public Bignum Denominator { get; set; }
}

class BigMeanr {
    public static int Main(string[] argv) {
        var sum = new RationalBignum(0);
        var n = new Bignum(0);
        using (var s = new FileStream(argv[0])) {
            using (var r = new BinaryReader(s)) {
                try {
                    while (true) {
                        var flt = r.ReadSingle();
                        rat = new RationalBignum(flt);
                        sum += rat;
                        n++;
                    }
                }
                catch (EndOfStreamException) {
                    break;
                }
            }
        }
        Console.WriteLine("The mean is: {0}", sum / n);
    }
}

请记住,那里有比你的编译器提供给你更多的数字类型。

答案 4 :(得分:5)

你可以将数据分成1000组数字,平均数,然后平均数。

答案 5 :(得分:4)

这是一个典型的分而治之的问题。

问题在于大量数字的平均值是相同的 作为集合的前半部分的平均值,与集合的后半部分的平均值平均。

换句话说:

AVG(A[1..N]) == AVG( AVG(A[1..N/2]), AVG(A[N/2..N]) )

这是一个简单的C#递归解决方案。 它通过了我的测试,应该是完全正确的。

public struct SubAverage
{
    public float Average;
    public int   Count;
};

static SubAverage AverageMegaList(List<float> aList)
{
    if (aList.Count <= 500) // Brute-force average 500 numbers or less.
    {
        SubAverage avg;
        avg.Average = 0;
        avg.Count   = aList.Count;
        foreach(float f in aList)
        {
            avg.Average += f;
        }
        avg.Average /= avg.Count;
        return avg;
    }

    // For more than 500 numbers, break the list into two sub-lists.
    SubAverage subAvg_A = AverageMegaList(aList.GetRange(0, aList.Count/2));
    SubAverage subAvg_B = AverageMegaList(aList.GetRange(aList.Count/2, aList.Count-aList.Count/2));

    SubAverage finalAnswer;
    finalAnswer.Average = subAvg_A.Average * subAvg_A.Count/aList.Count + 
                          subAvg_B.Average * subAvg_B.Count/aList.Count;
    finalAnswer.Count = aList.Count;

    Console.WriteLine("The average of {0} numbers is {1}",
        finalAnswer.Count, finalAnswer.Average);
    return finalAnswer;
}

答案 6 :(得分:3)

诀窍是你担心溢出。在这种情况下,这一切都归结为执行顺序。基本公式是这样的:

鉴于:

A = current avg
C = count of items
V = next value in the sequence
下一个平均值(A 1 )是:

      (C * A) + V
A1 =  ———————————
        C + 1

危险在于,您担心在评估序列的过程中,A应保持相对可控性,C会变得非常大。
最终C * A将溢出整数或双精度类型。

我们可以尝试的一件事就是像这样重写它,以减少溢出的可能性:

A1 = C/(C+1) * A/(C+1) + V/(C+1)

通过这种方式,我们永远不会乘以C * A而只处理较小的数字。但现在关注的是分工操作的结果。如果C非常大,则C/C+1(例如)在约束到正常浮点表示时可能没有意义。我能建议的最好是在这里使用C的最大类型。

答案 7 :(得分:2)

这是在伪代码中执行此操作的一种方法:

average=first
count=1
while more:
  count+=1
  diff=next-average
  average+=diff/count
return average

答案 8 :(得分:1)

对于迟到的评论感到抱歉,但是Joel Coehoorn提供的上述公式是不是错误地重写了?

我的意思是,基本公式是正确的:

  

假设:

     

A =当前平均值   C =项目数   V =序列中的下一个值

     

下一个平均值(A1)是:

     

A1 =((C * A)+ V)/(C + 1)

但不是:

  

A1 = C /(C + 1)* A /(C + 1)+ V /(C + 1)

我们不应该:

A1 = C /(C + 1)* A + V /(C + 1)

这可以解释kastermester的帖子:

“我的数学在这里结束 - 你有C,你说”走向无限“或者至少是一个非常大的数字,然后:C /(C + 1)走向1. A /(C + 1 )朝着0. V /(C + 1)走向0.总而言之:A1 ​​= 1 * 0 + 0所以很快就把A1推向0 - 似乎有点偏了。 - kastermester“

因为我们有A1 = 1 * A + 0,即A1走向A,这是正确的。

我一直在使用这种方法长时间计算平均值,上述精度问题对我来说从来都不是问题。

答案 9 :(得分:0)

为什么不在计算平均值之前缩放数字(下)?

答案 10 :(得分:0)

根据数字的范围,最好有一个数组,其中下标是你的数字,值是这个数字的数量,你可以从这个计算

答案 11 :(得分:0)

如果数字是int,则累计总数为long。如果数字很长......你用的语言是什么?在Java中,您可以在BigInteger中累积总数,BigInteger是一个整数,它将增长到需要的大小。您可以随时编写自己的类来重现此功能。它的要点只是制作一个整数数组来保存每个“大数字”。添加两个数字时,从低位值开始循环。如果加法结果设置了高位,则清除该位并将该位移到下一列。

另一种选择是一次找到1000个数字的平均值。保持这些中间结果,然后当你完成它们的所有平均值时。

答案 12 :(得分:0)

为什么浮点数的总和溢出?为了实现这一点,您需要使值接近最大浮点值,这听起来很奇怪。

如果你正在处理整数,我建议使用BigInteger,或者将集合分成多个子集,递归地平均子集,然后对平均值求平均值。

如果你正在处理花车,那会有点奇怪。滚动平均值可能变得非常不准确。我建议使用滚动平均值,只有在遇到溢出异常或集合结束时才会更新。因此,有效地将集合划分为非溢出集。

答案 13 :(得分:0)

我的两个想法:

  • 如果数字是整数,请使用像IntX这样的任意精度库 - 这可能太慢,但
  • 如果数字是浮点数并且您知道总金额,则可以将每个条目除以该数字并将结果相加。如果使用double,则精度应该足够。