如何以一般方式减少平均到子集的计算?

ang*_*son 5 language-agnostic math average

编辑:由于似乎没有人正在阅读这个链接的原始问题,让我在这里引入它的概要.

其他人提出的问题是,在给定大量值的情况下,总和将超过数据类型Double所包含的值,如何计算这些值的平均值.

有几个答案可以分别计算,例如取50和50个数字,然后计算这些集合内的平均值,然后最终得到所有这些集合的平均值并将它们组合起来得到最终的平均值.

我的立场是,除非你能保证所有这些值都可以分成许多大小相同的组,否则你不能使用这种方法.有人敢让我在这里问这个问题,以便提供答案,所以在这里.

基本上,给定任意数量的值,其中:

  • 我事先知道了数值(但是,如果不这样做,你的答案会怎样改变?`)
  • 我无法收集所有数字,也无法对它们求和(对于编程语言中的普通数据类型,总和太大了)

我该如何计算平均值?

这里的其余部分概述了分割成同等大小的方法的方法和问题,但我真的只想知道如何做到这一点.

请注意,我完全清楚数学知道,在数学理论术语中,计算总和A[1..N]/N会给我平均值,让我们假设有理由说它不是那么简单,我需要分担工作量,并且值的数量不一定能分为3,7,50,1000或其他任何值.

换句话说,我所追求的解决方案必须是通用的.


从这个问题:

我的立场是将工作量分成几组并不好,除非你能确保这些集的大小相等.


编辑:最初的问题是关于特定数据类型可以容纳的上限,并且因为他总结了很多数字(例如给出的计数是10 ^ 9),所以数据类型无法保持总和.由于这是原始解决方案中的一个问题,我假设(这是我的问题的先决条件,很抱歉错过了)这些数字太大而无法给出任何有意义的答案.

因此,直接除以值的总数.原因SUM/COUNT解决方案出来的原因是SUM会溢出,但我们假设,对于这个问题,SET-SET/SET-SIZE会下溢,或者其他什么.

重要的是我不能简单地总结,我不能简单地除以总值的数量.如果我不能这样做,我的方法是否有效,我能做些什么来解决它?


让我概述一下这个问题.

假设你要计算1到6的数字的平均值,但你不能(无论出于何种原因)通过对数字求和,计算数字,然后将总和除以计数来做到这一点.换句话说,你不能简单地做(1 + 2 + 3 + 4 + 5 + 6)/ 6.

换句话说,SUM(1..6)/COUNT(1..6)就是出局.我们在这里不考虑NULL(如在数据库NULL中).

该问题的几个答案提到能够将数字平均分成集合,比如3或50或1000个数字,然后为此计算一些数字,然后最终组合这些数值以获得最终平均值.

我的立场是,在一般情况下这是不可能的,因为这会使一些数字,即最终集合中出现的数字,或多或少比之前集合中的所有数字都有价值,除非您可以将所有数字分成均等大小的集合.

例如,要计算1-6的平均值,您可以将其拆分为3个数字的集合,如下所示:

/ 1   2   3 \   / 4   5   6 \
| - + - + - | + | - + - + - |
\ 3   3   3 /   \ 3   3   3 /  <-- 3 because 3 numbers in the set
 ----------      -----------
      2               2        <-- 2 because 2 equally sized groups
Run Code Online (Sandbox Code Playgroud)

哪个给你这个:

      2               5
      -       +       - = 3.5
      2               2
Run Code Online (Sandbox Code Playgroud)

(注意:(1 + 2 + 3 + 4 + 5 + 6)/ 6 = 3.5,所以这里这是正确的)

但是,我的观点是,一旦值的数量不能分成多个大小相同的集合,这种方法就会崩溃.例如,序列1-7怎么样,它包含素数值.

可以采用类似的方法,不会将所有值相加,并一次性计算所有值吗?

那么,有这样的方法吗?如何计算以下成立的任意数量的值的平均值:

  1. 无论出于何种原因,我无法做出正常的金额/计数方法
  2. 我事先知道了数值(如果我没有,那会改变答案吗?)

Dan*_*ral 7

好吧,假设您添加了三个数字并除以三,然后添加两个数字并除以2.你能从中获得平均值吗?

x = (a + b + c) / 3
y = (d + e) / 2
z = (f + g) / 2
Run Code Online (Sandbox Code Playgroud)

你想要的

r = (a + b + c + d + e + f + g) / 7
Run Code Online (Sandbox Code Playgroud)

这等于

r = (3 * (a + b + c) / 3 + 2 * (d + e) / 2 + 2 * (f + g) / 2) / 7
r = (3 * x + 2 * y + 2 * z) / 7
Run Code Online (Sandbox Code Playgroud)

当然,上面的两条线都溢出了,但是由于分裂是分配的,我们这样做

r = (3.0 / 7.0) * x + (2.0 / 7.0) * y + (2.0 / 7.0) * z
Run Code Online (Sandbox Code Playgroud)

这保证你不会溢出,因为我将x,y和z乘以小于1的分数.

这是基本点.我既没有预先将所有数字除以总数,也没有超过溢出数.

所以......如果你继续添加累加器,跟踪你添加了多少个数字,并且总是测试下一个数字是否会导致溢出,你可以获得部分平均值,并计算最终平均值.

不,如果您事先不知道这些值,它不会改变任何东西(前提是您可以在求和时计算它们).

这是一个Scala函数.这不是惯用的Scala,因此可以更容易理解:

def avg(input: List[Double]): Double = {
  var partialAverages: List[(Double, Int)] = Nil
  var inputLength = 0
  var currentSum = 0.0
  var currentCount = 0
  var numbers = input

  while (numbers.nonEmpty) {
    val number = numbers.head
    val rest = numbers.tail
    if (number > 0 && currentSum > 0 && Double.MaxValue - currentSum < number) {
      partialAverages = (currentSum / currentCount, currentCount) :: partialAverages
      currentSum = 0
      currentCount = 0
    } else if (number < 0 && currentSum < 0 && Double.MinValue - currentSum > number) {
      partialAverages = (currentSum / currentCount, currentCount) :: partialAverages
      currentSum = 0
      currentCount = 0
    }
    currentSum += number
    currentCount += 1
    inputLength += 1
    numbers = rest
  }
  partialAverages = (currentSum / currentCount, currentCount) :: partialAverages

  var result = 0.0
  while (partialAverages.nonEmpty) {
    val ((partialSum, partialCount) :: rest) = partialAverages
    result += partialSum * (partialCount.toDouble / inputLength)
    partialAverages = rest
  }

  result
}
Run Code Online (Sandbox Code Playgroud)

编辑:不会乘以2和3,让我回到"不支持数据类型?"的范围内.

不,如果你最后在7点潜水,绝对是.但是在这里你要划分总和的每一步.即使在您的实际情况下,权重(2/73/7)也将在可管理数字(例如1/101/10000)的范围内,与您的体重(即1)相比,这不会产生很大的差异.

PS:我想知道为什么我正在研究这个答案,而不是写我的,我可以赢得我的代表:-)