Ruby中的几何平均值为大型数组返回Infinity

时间:2013-02-07 05:36:40

标签: ruby math mean

我正在尝试计算Ruby中数组的几何平均值。但是,在数组增长到一定大小后,它开始返回Infinity以计算几何平均值。知道导致这种情况发生的原因,或者更好的方法来计算Ruby中较大数组的几何平均值吗?

ruby​​ 1.9.3p125(2012-02-16修订版34643)[x86_64-darwin10.8.0]

示例

def gmean(x)
  prod=1.0
  x.each {|v| prod *= v}
  prod**(1.0/x.size)
end 

sample_array = [1, 1, 1, 1200, 1483, 1827, 2114, 2163, 2231, 2313, 2368, 2636, 2736, 2834, 2847, 2985, 3225, 3304, 3317, 3439, 3519, 3586, 3607, 3611, 3722, 3770, 4346, 4383, 4392, 4548, 4639, 4773, 4836, 4929, 4991, 4998, 5075, 5078, 5433, 5549, 5727, 5908, 5911, 5989, 6007, 6031, 6065, 6097, 6141, 6654, 6915, 6969, 6972, 7074, 7257, 7260, 7342, 7526, 7550, 7898, 8032, 8037, 8265, 8567, 8888, 9033, 9169, 9412, 9701, 9962, 10247, 11209, 14069, 14741, 15088, 15511, 18775, 19755, 19937, 24064, 32437, 41372, 59057, 778335]
puts gmean(sample_array)
puts sample_array.inject{|sum,x| sum + x }
puts sample_array.length

#=> 4672.4331716807965
#=> 1429766
#=> 84

sample_array2 = [1, 1, 2, 1200, 1483, 1827, 2114, 2163, 2231, 2313, 2368, 2636, 2736, 2834, 2847, 2985, 3225, 3304, 3317, 3439, 3519, 3586, 3607, 3611, 3722, 3770, 4346, 4383, 4392, 4548, 4639, 4773, 4836, 4929, 4991, 4998, 5075, 5078, 5433, 5549, 5727, 5908, 5911, 5989, 6007, 6031, 6065, 6097, 6141, 6654, 6915, 6969, 6972, 7074, 7257, 7260, 7342, 7526, 7550, 7898, 8032, 8037, 8265, 8567, 8888, 9033, 9169, 9412, 9701, 9962, 10247, 11209, 14069, 14741, 15088, 15511, 18775, 19755, 19937, 24064, 32437, 41372, 59057, 778335]
puts gmean(sample_array2)
puts sample_array2.inject{|sum,x| sum + x }
puts sample_array2.length

#=> Infinity
#=> 1429767
#=> 84

2 个答案:

答案 0 :(得分:7)

如果BigDecimal最终占用太多内存,您可以利用对数:

def gmean(x)
  sum = x.inject(0) { |memo, v| memo + Math.log(v) }
  sum /= x.size
  Math.exp(sum).round(2)
end

答案 1 :(得分:4)

Float可以容纳你的价值。而是考虑使用BigDecimal

require 'bigdecimal'

def gmean(x)
  prod = BigDecimal.new 1
  x.each { |v| prod *= BigDecimal.new(v) }
  prod ** (1.0 / x.size)
end

gmean(sample_array2).to_f  #=> 4711.148446895203

请注意,您的方法可以简化为更实用的样式:

def gmean(xs)
  one = BigDecimal.new 1
  xs.map { |x| BigDecimal.new x }.inject(one, :*) ** (one / xs.size)
end