对不断增长的数组进行内存有效的下采样(绘图)

时间:2016-09-11 12:21:51

标签: javascript algorithm memory charts sampling

我的节点进程每半秒收到一个采样点,我想更新我收到的所有采样点的历史图表。 图表应该是一个数组,其中包含从0到当前点的所有点的下采样历史记录。 换句话说,数组的最大长度应为l。如果我收到的样本点数多于l,我希望图表数组是整个历史记录的缩减采样到l版本。

用代码表达:

const CHART_LENGTH = 2048
createChart(CHART_LENGTH)
onReceivePoint = function(p) {
    // p can be considered a number
    const chart = addPointToChart(p)
    // chart is an array representing all the samples received, from 0 to now
    console.assert(chart.length <= CHART_LENGTH)
}

我已经有一个带数字数组的工作下采样功能:

function downsample (arr, density) {
  let i, j, p, _i, _len
  const downsampled = []
  for (i = _i = 0, _len = arr.length; _i < _len; i = ++_i) {
    p = arr[i]
    j = ~~(i / arr.length * density)
    if (downsampled[j] == null) downsampled[j] = 0
    downsampled[j] += Math.abs(arr[i] * density / arr.length)
  }
  return downsampled
}

这样做的一个简单方法显然是将我收到的所有点保存到数组中,并在数组增长时应用downsample函数。这可行,但是,由于这段代码将在服务器中运行,可能连续数月和数月,最终会使支持数组增长太多,以至于进程内存不足。

问题是:有没有办法构建图表数组,重新使用图表本身的先前内容,以避免维持不断增长的数据结构?换句话说,这个问题是否存在恒定的内存复杂性解决方案?

请注意,图表必须包含自采样点#0以来的整个历史记录,因此不能接受最后n个点的图表。

1 个答案:

答案 0 :(得分:1)

唯一不会使数据失真并且可以多次使用的操作是整数个相邻样本的聚合。你可能想要2。​​

更具体地说:如果您发现添加新样本将超出数组边界,请执行以下操作:从数组的开头开始并平均两个后续样本。这会将数组大小减少2,并且您有空间添加新样本。这样做,您应该跟踪当前的簇大小c(构成阵列中一个条目的样本量)。你从一开始。每次减少都会将群集大小乘以2。

现在的问题是你不能再将新样本直接添加到数组中,因为它们具有完全不同的比例。相反,您应该将下一个c样本平均为新条目。事实证明,在当前集群中存储样本数n就足够了。因此,如果您添加新样本s,则可以执行以下操作。

n++
if n = 1
    append s to array
else        
    //update the average
    last array element += (s - last array element) / n
if n = c
    n = 0 //start a new cluster

所以你真正需要的内存如下:

  • 具有预定义长度的历史数组
  • 历史数组中的元素数
  • 当前群集大小c
  • 当前群集中的元素数量n

附加内存的大小不依赖于样本总数,因此O(1)