【问题标题】:Formula to calculate average number on expanding data array计算扩展数据数组的平均数的公式
【发布时间】:2013-05-23 21:54:09
【问题描述】:

说,如果我有一个最终的数字数组,说:

{1, 5, 7, 2}

他们的平均值是:

(1 + 5 + 7 + 2) / 4;   //Or, sum of all elements, divided by their number

但是,如果我的数组不断增长,并且我需要知道当前的平均数,此时整个数组还不知道。你是怎么计算的?

比如说,当我试图显示当前的数据传输率时。

【问题讨论】:

标签: c# rate


【解决方案1】:

我会采用一种简单的方法来获得具有恒定空间复杂度 (1) 的 running total(累积)。根据 avg 的请求,返回结果为running total/total number of item。没有延长时间复杂度,因为我们不会在之后迭代数组来找到累积和。

【讨论】:

  • 谢谢。这个概念的一个明显缺点是包含running total 的变量溢出。你会如何解决这个问题?
  • @ahmd0 然后将结果存储在 larger 类型、long、double、decimal、Biginteger 等中。
  • @I4V:我已经在使用double,但我担心随着时间的推移我可能会遇到问题......我认为它越大越精确。
  • @ahmd0 BigInteger 怎么样?
  • @I4V:是的,我考虑过,但是我会在开始时用较小的数字失去精度……但是,也许我应该根据总数的大小同时使用两者。嗯……
【解决方案2】:

每次获得新值时,更新总和和计数,并在需要向用户显示它们时简单地将两者相除。

对于数据传输率,这是有问题的方法。想一想您开始使用高带宽传输然后连接断开的场景,使用简单的平均值,您的 UI 可能需要很长时间才能反映当前传输速率为 0。 使用加权移动平均线是一种让您的 UI 看起来更具响应性的快速方法。

最简单的实现方式是定期(比如每 5 秒)对传输速率进行采样,并使用如下方式计算您的速率:

 float weight = 2.0; //you are going to want to tweak this to get the right balance between "responsive" and "noisy"

 void UpdateAverage(float newValue)
 {
    this.Average = (this.Average + (newValue*weight))/(weight+1)
 }

【讨论】:

  • 简单的数学答案
  • 谁投了反对票,你能解释一下原因吗?这也是我的想法。
【解决方案3】:

我知道我在这里聚会迟到了,但我在 2000 年解决了一个类似的问题,当时我正在创建一个自适应负载平衡器。标准平均值有两个问题,都在给出的答案中提到:

  1. 溢出场景
  2. - 保持累计会导致溢出
  3. 重新计算平均值
  4. - 通常,您必须对每个样本都执行此操作

因此,您可以将正常平均值计算转换为数学家所说的“递归关系”,这意味着您存储以前的平均值,然后用于计算新的平均值(类似于 Yaur 的答案,我必须同意艾哈迈德,我看不出它被否决了)。那天我在 Delphi 中编写了原件,所以我最近在 .NET 中再次做了同样的事情,并将其与随着项目列表变大而计算连续平均值的标准过程进行了比较。

不想自我推销(我发布链接只是为了节省我的打字手指),您可以在以下位置找到理论处理、代数、比较实验的结果以及它如何解决问题:

http://goadingtheitgeek.blogspot.co.uk/2014/05/blast-from-past.html

我希望你觉得它有用。

【讨论】:

    【解决方案4】:

    您正在寻找移动平均线:

        static void Main(string[] args) {
            var nums = Enumerable.Range(1, 5).Select(n => (double)n);
            nums = nums.Concat(nums.Reverse());
    
            var sma3 = SMA(3);
            var sma5 = SMA(5);
    
            foreach (var n in nums) {
                Console.WriteLine("{0}    (sma3) {1,-16} (sma5) {2,-16}", n, sma3(n), sma5(n));
            }
        }
    
        static Func<double, double> SMA(int p) {
            Queue<double> s = new Queue<double>(p);
            return (x) => {
                if (s.Count >= p) {
                    s.Dequeue();
                }
                s.Enqueue(x);
                return s.Average();
            };
        }
    

    来源:http://rosettacode.org/wiki/Averages/Simple_moving_average#C.23

    【讨论】:

    • 谢谢。 “移动平均线”假设我将整个数据数组保存在内存中的某个位置,对吗?抱歉,该 Wiki 页面太复杂,无法快速掌握。
    • 您不必将整个数组保存在内存中,只需将您要在平均值中使用的样本数保存。
    【解决方案5】:

    我敢打赌,你想要一些快速的东西,否则你已经有了答案。将您已经拥有的所有数字与您已经拥有的数组的长度相加。这很简单。

    但有时您无法知道数组是否有界,它可能是无限的,例如来自麦克风的数据。在这种情况下,移动平均线的建议很好,这意味着您必须从数组中获取最后的 x 值并仅计算这些值的平均值。无论您有 x 值还是 1000x 值,算法和计算结果所需的时间都保持不变。

    编辑:

    x vs 1000x 来自算法复杂性。假设您将 5 个数字相加,即 5 次运算,然后除以 5,再进行一次运算,总共 6(为了示例,我们假设它们都花费相同的计算机时间,但实际上除法很慢与添加相比)。如果你使用相同的代码但有 1000 个数字,你会执行 1001 次操作,这将比第一种情况花费更多的时间!

    使用“移动平均线”,您始终采用固定数量的数字,因此无论您有 5 个还是 1000 个数字,您的算法都需要固定的时间来执行。

    移动平均线只是一种花哨的措辞,它表示您不会从一次到另一次在数组中采用相同的数字。想象一下以下数组:

    int x = { 1, 4, 6, 3, 1 };
    int arrayLength = 5;

    那么这个数组的平均值就是

    int runningTotal = 0;
    for(int i = 0; i < arrayLength; i++)
    {
       runningTotal += x[i];
    }
    double average = runningTotal / arrayLength
    

    3 个值的移动平均值将是

    int movingLength = 3;
    int runningTotal = 0;
    for(int i = 0; i < movingLength; i++)
    {
       runningTotal += x[arrayLength - i - 1];
    }
    double average = runningTotal / movingLength;
    

    所以当数组增长时,数组中的第一个值不是计算的一部分。

    【讨论】:

    • 你知道我还在努力理解“移动平均线”的概念,所以我不确定你在最后的 x 值或 1000x 值比较中是什么意思?是的,我正在寻找不需要每次计算平均值时都需要整个数组的东西。
    • 感谢您的解释。我现在明白了。这就好像我丢弃了数组的头部(或早期数据)并仅计算最后 N 个元素的平均值。唔。这是一个有趣的想法......
    【解决方案6】:

    假设您想要移动平均线..

    您需要跟踪上次计算平均值时元素的当前总和。

    如果你的数组是这样的......

    Array = {1, 5, 7, 2}
    Sum = 1 + 5 + 7 + 2 = 15
    Number of elements = 4
    Average = Sum / Number of elements = 3.75
    

    你添加了几个元素,你的数组看起来像这样......

    Array = {1, 5, 7, 2, 10, 6}
    

    假设您的实际数组要大得多...重新计算您的平均值..

    Sum = ([previous sum] + [sum of new elements]) / [number of elements]
    Number of elements = 6
    Average = ((15 * 4) + 10 + 6) / 6 = 5.1666667
    

    编辑:如果您担心精度和大小,请查看BigInteger

    【讨论】:

    • 谢谢。是的,我只需要一个大约。平均的。那么这种情况下的公式是什么?
    • 只要保持你的运行总数和元素数量......你只需要跟踪自上次计算运行总数以来哪些元素是数组中的新元素。如果您要将元素添加到数组中,那么只需在添加它们时添加到您的运行总数中
    • 您将如何解决运行总数中的溢出问题?
    • 您希望您的数字有多大?如果你需要它是一个整数,你总是可以使用一个 BigInteger。
    • 我不知道。如果转移需要很长时间,我可以想象总数可能会变得很大。
    【解决方案7】:

    如果平均缓冲区长度是可变的,我的旧程序就是这样。

    namespace WindowsFormsApplication1
    {
      public partial class ComTester : Form
      {
        public int []sAvgArr=new int [251];
        public int sAvgAdr,sAvgCnt;
    
        private void Calc_Values(object sender, int v)//v is value
        {
            int n = AverageLength;
            if (n > 250) n = 250;//average buffer maksimum
            if (n > 0)
            {
                sAvgArr[sAvgAdr] = v;
                sAvgAdr += 1;
                sAvgCnt += 1;
                if (n <= sAvgAdr) sAvgAdr = 0;
                if (n <= sAvgCnt) sAvgCnt = n;
                n = sAvgCnt;
                int f = 0, l = 0; ;
                for (l = 0; l < n; l += 1)
                    f += sAvgArr[l];
                f = f / sAvgCnt;
                sAvgVal = f;
            }
            else 
            {
                sAvgVal=v;
            }
        }
      }
    }
    

    【讨论】:

      【解决方案8】:
      using System;
      
      namespace ConsoleApplication1
      {
          class Program
          {
              static void Main(string[] args)
              {
                  int  n = 3 , x , sum = 0 ;
                  double ave;
      
                  for (int i = 0; i < n; i++ )
                  {
                      Console.WriteLine("enter the number:");
                      x = Convert.ToInt16(Console.ReadLine());
                      sum += x;
                  }
      
                  ave = (double)(sum) / 3;
                  Console.WriteLine("average:");
                  Console.WriteLine( ave );
                  Console.ReadLine();
              }
          }
      }
      

      【讨论】:

      • 请考虑修正答案的格式,并提供一些描述/解释它的文字。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-14
      • 2023-03-14
      • 2019-04-08
      • 1970-01-01
      • 2013-10-25
      • 1970-01-01
      相关资源
      最近更新 更多