4

我正在尝试实现一个以列表为参数的累积加权平均函数

[[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]]

并返回(此处四舍五入到小数点后 2 位)

[3.1, 2.47, 4.08, 5.81]

例如:2.47 = (1000 * 3.1 + 500 * 1.2) / 1500。

我目前已经使用以下代码解决了这个问题:

def cumulative_weighted_average(list)
  cs = 0
  qu = 0
  res = list.inject([0]) do |s, el|
    cs += el[0] * el[1]
    qu += el[0]
    s + [cs.to_f / qu]
  end
  res.shift
  res
end

有没有更短(更紧凑)的方式来做到这一点?

编辑: 感谢以下答案!该列表平均包含大约 1000 个条目,因此不确定速度要求。由于我需要能够从本质上跟踪块中的两个值,是否有一些注入的扩展允许您编写

list.inject([0,0]){ |s1, s2, el| ...}

s1 和 s2 在哪里初始化为 0?

4

3 回答 3

5

我认为这就是你想要的:

def cumulative_weighted_average list
  cs, qu = 0.0, 0.0
  list
  .map{|x, w| [cs += x * w, qu += x]}
  .map{|cs, qu| cs / qu}
end

cumulative_weighted_average([[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]])
# => [3.1, 2.466666666666667, 4.078260869565217, 5.812222222222222]


对于附加问题,可能会有这样的事情:

list.inject([0,0]){|(s1, s2), el| ...}
于 2013-07-19T17:00:53.253 回答
2

有没有一种短路(更紧凑)的方式来做到这一点?

我可以给你试试。。

arr = [[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]]
arr2 = (1..arr.size).map do |i| 
  b = arr.take(i)
  b.reduce(0){|sum,a| sum + a.reduce(:*)}/b.reduce(0){|sum,k| sum + k[0]}
end
arr2
# => [3.1, 2.466666666666667, 4.078260869565217, 5.812222222222222]
于 2013-07-19T16:04:19.060 回答
0

如果您允许两阶段计算(不一定较慢,涉及相同数量的数学),您可以避免“外部”临时变量,并使事情看起来更干净和惯用的 Ruby:

def cumulative_weighted_average list
  cumulative_totals = list.inject( [] ) do |cumulative,item|
    tot_count, tot_sum = cumulative.last || [0, 0.0]
    next_count, next_value = item
    cumulative << [ tot_count + next_count,  tot_sum + next_count * next_value ]
  end
  cumulative_totals.map { |count,sum| sum/count }
end

p cumulative_weighted_average( 
    [[1000, 3.1], [500, 1.2], [800, 7.1], [1300, 8.88]] )

=> [3.1, 2.46666666666667, 4.07826086956522, 5.81222222222222]
于 2013-07-19T16:10:52.480 回答