对于长数组,似乎#sum比#reduce快,而对于短数组,它们基本相同。
def reduce_t(s,f)
start = Time.now
puts (s..f).reduce(:+) #Printing the result just to make sure something is happening.
finish = Time.now
puts finish - start
end
def sum_t(s,f)
start = Time.now
puts (s..f).sum
finish = Time.now
puts finish - start
end
irb(main):078:0> sum_t(1,10); reduce_t(1,10)
55
0.000445
55
0.000195
=> nil
irb(main):079:0> sum_t(1,1000000); reduce_t(1,1000000)
500000500000
8.1e-05
500000500000
0.101487
=> nil
除了速度之外还有其他考虑因素吗?有没有什么情况可以更好地使用#reduce而不是#sum来完成相同的结束,这是一个简单的总和?
修改
mu is too short正确地指出,在得出关于时间结果的结论之前,我应该进行多次迭代。我没有使用Benchmark
,因为我还不熟悉它,但我希望我在下面写的内容足够且令人信服。
def sum_reduce_t(s,f)
time_reduce = 0
time_sum = 0
reduce_faster = 0
sum_faster = 0
30.times do
start_reduce = Time.now
(s..f).reduce(:+)
finish_reduce = Time.now
time_reduce += (finish_reduce - start_reduce)
start_sum = Time.now
(s..f).sum
finish_sum = Time.now
time_sum += (finish_sum - start_sum)
if time_sum > time_reduce
reduce_faster += 1
else
sum_faster += 1
end
end
puts "Total time (s) spent on reduce: #{time_reduce}"
puts "Total time (s) spent on sum: #{time_sum}"
puts "Number of times reduce is faster: #{reduce_faster}"
puts "Number of times sum is faster: #{sum_faster}"
end
irb(main):205:0> sum_reduce_t(1,10)
Total time (s) spent on reduce: 0.00023900000000000004
Total time (s) spent on sum: 0.00015400000000000003
Number of times reduce is faster: 0
Number of times sum is faster: 30
=> nil
irb(main):206:0> sum_reduce_t(1,100)
Total time (s) spent on reduce: 0.0011480000000000004
Total time (s) spent on sum: 0.00024999999999999995
Number of times reduce is faster: 0
Number of times sum is faster: 30
=> nil
irb(main):207:0> sum_reduce_t(1,1000)
Total time (s) spent on reduce: 0.004804000000000001
Total time (s) spent on sum: 0.00019899999999999996
Number of times reduce is faster: 0
Number of times sum is faster: 30
=> nil
irb(main):208:0> sum_reduce_t(1,10000)
Total time (s) spent on reduce: 0.031862
Total time (s) spent on sum: 0.00010299999999999996
Number of times reduce is faster: 0
Number of times sum is faster: 30
=> nil
irb(main):209:0> sum_reduce_t(1,100000)
Total time (s) spent on reduce: 0.286317
Total time (s) spent on sum: 0.00013199999999999998
Number of times reduce is faster: 0
Number of times sum is faster: 30
=> nil
irb(main):210:0> sum_reduce_t(1,1000000)
Total time (s) spent on reduce: 2.7116779999999996
Total time (s) spent on sum: 0.00021200000000000008
Number of times reduce is faster: 0
Number of times sum is faster: 30
=> nil
我的问题仍然存在:是否有时候使用#reduce代替#sum?
答案 0 :(得分:5)
使用sum
的行为和结果与inject &:+
不同的一种方法是在对浮点值求和时。
如果您将一个大的浮点值添加到一个小浮点值,通常结果与较大的浮点值相同:
> 99999999999999.98 + 0.001
=> 99999999999999.98
这可能会在添加浮点数时导致错误,因为较小的值实际上会丢失,即使它们很多也是如此。
例如:
> a = [99999999999999.98, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001]
=> [99999999999999.98, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001]
> a.inject(&:+)
=> 99999999999999.98
在此示例中,您可以根据需要随时添加0.001
,它永远不会更改结果的值。
Ruby’s implementation of sum
在对浮点数求和时使用Kahan summation algorithm来减少此错误:
> a.sum
=> 100000000000000.0
(注意结果在这里,你可能期望在.99
中结束,因为数组中有10 0.001
。这只是正常的浮点行为,也许我应该试着找到一个更好的例子。重要的一点是,当你添加许多小值时总和会增加,inject &:+
不会发生这种情况。)
答案 1 :(得分:1)
好的,我认为这些评论可以结合起来形成一个好的答案,但如果有人想更深入地解释,我肯定会接受其他人。
关键点是:
reduce(:-)
,reduce(:*)
等等),因此不应该被视为无用。如果求和方法可用,它不是总和巨大数组的最佳选择。我的问题的直接答案是: