红宝石的累积加权平均值

时间:2013-07-19 15:45:53

标签: ruby

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

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

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

[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?

3 个答案:

答案 0 :(得分: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| ...}

答案 1 :(得分: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]

答案 2 :(得分:0)

你可以避免使用“外部”临时变量,并使事情看起来更清晰,并且惯用Ruby,如果你允许进行两阶段计算(这不一定比较慢,涉及相同数量的数学): / p>

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]