如何从数组中求平均值?
如果我有一个数组:
[0,4,8,2,5,0,2,6]
平均得到3.375。
如何从数组中求平均值?
如果我有一个数组:
[0,4,8,2,5,0,2,6]
平均得到3.375。
当前回答
让我在竞争中引入一些可以解决零除问题的东西
a = [1,2,3,4,5,6,7,8]
a.reduce(:+).try(:to_f).try(:/,a.size) #==> 4.5
a = []
a.reduce(:+).try(:to_f).try(:/,a.size) #==> nil
但是,我必须承认,“try”是一个Rails助手。但你可以很容易地解决这个问题:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
顺便说一句:我认为空列表的平均值为零是正确的。零的平均值是零,不是0。这就是预期行为。但是,如果您更改为:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
空数组的结果不会像我预期的那样是一个异常,而是返回NaN…我在Ruby中从未见过这种情况。;-)似乎是Float类的特殊行为…
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
其他回答
我非常喜欢定义一个mean()方法,这样我的代码更有表现力。
默认情况下我通常会忽略nil,这是我定义的
def mean(arr)
arr.compact.inject{ |sum, el| sum + el }.to_f / arr.compact.size
end
mean([1, nil, 5])
=> 3.0
如果您想保留nils,只需删除两个.compact。
为了让公众开心,还有另一个解决方案:
a = 0, 4, 8, 2, 5, 0, 2, 6
a.reduce [ 0.0, 0 ] do |(s, c), e| [ s + e, c + 1 ] end.reduce :/
#=> 3.375
这台电脑上没有ruby,但在这个程度上应该可以工作:
values = [0,4,8,2,5,0,2,6]
total = 0.0
values.each do |val|
total += val
end
average = total/values.size
试试这个:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
请注意.to_f,它可以避免整数除法带来的任何问题。你还可以:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
您可以像另一位评论者建议的那样将其定义为Array的一部分,但您需要避免整数除法,否则您的结果将是错误的。而且,这并不适用于所有可能的元素类型(显然,平均值只适用于可以求平均值的东西)。但如果你想走这条路,可以用这个:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
如果您以前没有见过inject,那么它并没有看起来那么神奇。它遍历每个元素,然后对其应用累加器值。然后将累加器传递给下一个元素。在本例中,累加器只是一个反映之前所有元素之和的整数。
编辑:评论者戴夫·雷提出了一个很好的改进。
编辑:评论者格伦·杰克曼的提议,使用arr.inject(:+)。To_f也很好,但如果你不知道发生了什么,可能有点太聪明了。:+是一个符号;当传递给inject时,它对每个元素对累加器值应用由符号命名的方法(在本例中为加法操作)。
我不喜欢公认的解决方案
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
它并不是纯粹的功能性的。 我们需要一个变量来计算。最后的尺寸。
为了从函数上解决这个问题我们需要关注两个 值:所有元素的和,以及元素的个数。
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh改进了这个解决方案:不是参数r是一个数组,我们可以使用解构立即将它分解为两个变量
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
如果你想看看它是如何工作的,添加一些看跌期权:
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
r2 = [ sum + ele, size + 1 ]
puts "adding #{ele} gives #{r2}"
r2
end.inject(:/)
adding 5 gives [5.0, 1]
adding 6 gives [11.0, 2]
adding 7 gives [18.0, 3]
adding 8 gives [26.0, 4]
=> 6.5
我们也可以使用结构体而不是数组来包含sum和count,但我们必须先声明结构体:
R=Struct.new(:sum, :count)
[5, 6, 7, 8].inject( R.new(0.0, 0) ) do |r,ele|
r.sum += ele
r.count += 1
r
end.inject(:/)