简体   繁体   English

Ruby:使用数组和循环 - 简单的移动平均线

[英]Ruby: Working with Arrays & Loops - The Simple Moving Average

working with simple moving averages in ruby, and came up with this code, specifically a three-day moving average:在 ruby​​ 中使用简单的移动平均线,并提出了以下代码,特别是三天移动平均线:

 a = [3, 3, 3, 3, 5, 9, 7, 8, 9, 11]
 sma = []

 for i in 2 ... 9 do
   sma.push((a[i] + a[i-1] + a[i-2])/3.0)
 end

This code passes and it seems simple enough, however, what if the array contains over hundreds of items?这段代码通过了,看起来很简单,但是,如果数组包含数百个项目怎么办? How would I add 50 items for a 50-day average?我如何为 50 天的平均值添加 50 个项目? Is there a different approach or should another loop be nested into the code?是否有不同的方法或应该将另一个循环嵌套到代码中?

*Also, I understand there are gems out there for this type of operation, but I'm more interested in creating this from scratch. *此外,我知道这种类型的操作有一些宝石,但我更感兴趣的是从头开始创建它。

The method Enumerable#each_cons is perfect for this calculation:方法Enumerable#each_cons非常适合这种计算:

def moving_average(a, ndays, precision)
  a.each_cons(ndays).map { |e| e.reduce(&:+).fdiv(ndays).round(precision) }
end
a = [3, 4, 1, 2, 5, 9, 7, 8, 9, 11]

moving_average(a,3,2)
  #=> [2.67, 2.33, 2.67, 5.33, 7.0, 8.0, 8.0, 9.33] 

For this example,对于这个例子,

enum = a.each_cons(3) 
  #=> #<Enumerator: [3, 4, 1, 2, 5, 9, 7, 8, 9, 11]:each_cons(3)> 

The values the enumerator enum passes into the block can be obtained by converting enum to an array:枚举器enum传递到块中的值可以通过将enum转换为数组来获得:

enum.to_a 
  #=> [[3, 4, 1], [4, 1, 2], [1, 2, 5], [2, 5, 9],
  #    [5, 9, 7], [9, 7, 8], [7, 8, 9], [8, 9, 11]]

map then converts each of these elements to an average. map然后将这些元素中的每一个转换为平均值。


If a and ndays are large, greater efficiency can be achieved as follows.如果andays很大,可以实现更高的效率,如下所示。

def moving_average(a,ndays,precision)
  (0..a.size-ndays-1).each_with_object([a[0,ndays].sum]) do |i,arr|
    arr << arr.last - a[i] + a[i+ndays]
  end.map { |tot| tot.fdiv(ndays).round(precision) }
end

moving_average(a,3,2)
  #=> [2.67, 2.33, 2.67, 5.33, 7.0, 8.0, 8.0, 9.33] 

After totaling合计后

tot1 = [3, 4, 1].sum
  #=> 8

which is needed to compute the first 3-day moving average, the total of [4, 1, 2] , needed to compute the second 3-day moving average, is calculated as follows:这是计算第一个 3 天移动平均线所需的总和[4, 1, 2] ,计算第二个 3 天移动平均线所需的计算如下:

tot2 = tot1 + 2 - 3
  #=> 7

where 2 is the last element of [4, 1, 2] and 3 is the first element of [3, 4, 1] .其中2[4, 1, 2]的最后一个元素, 3[3, 4, 1]的第一个元素。

While there was no time savings for this example it can be seen that computing each total from the previous one would save time when a and ndays are large, as the computational complexity is reduced from O(n^2) to O(n) , where n = a.size .虽然这个例子没有节省时间,但可以看出,当andays很大时,计算前一个的每个总数会节省时间,因为计算复杂度从O(n^2)O(n) ,其中n = a.size

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM