Risposta breve
Per un intervallo intero:
Enumerable#sum
ritorna (range.max-range.min+1)*(range.max+range.min)/2
Enumerable#inject(:+)
scorre su ogni elemento.
Teoria
La somma di numeri interi tra 1 ed n
è chiamata numero triangolare ed è uguale a n*(n+1)/2
.
La somma degli interi tra n
e m
è il numero triangolare di m
meno il numero triangolare di n-1
, che è uguale a m*(m+1)/2-n*(n-1)/2
e può essere scritto (m-n+1)*(m+n)/2
.
Enumerable # sum in Ruby 2.4
Questa proprietà è utilizzata Enumerable#sum
per intervalli interi:
if (RTEST(rb_range_values(obj, &beg, &end, &excl))) {
if (!memo.block_given && !memo.float_value &&
(FIXNUM_P(beg) || RB_TYPE_P(beg, T_BIGNUM)) &&
(FIXNUM_P(end) || RB_TYPE_P(end, T_BIGNUM))) {
return int_range_sum(beg, end, excl, memo.v);
}
}
int_range_sum
Somiglia a questo :
VALUE a;
a = rb_int_plus(rb_int_minus(end, beg), LONG2FIX(1));
a = rb_int_mul(a, rb_int_plus(end, beg));
a = rb_int_idiv(a, LONG2FIX(2));
return rb_int_plus(init, a);
che equivale a:
(range.max-range.min+1)*(range.max+range.min)/2
l'uguaglianza di cui sopra!
Complessità
Grazie mille a @k_g e @ Hynek-Pichi-Vychodil per questa parte!
somma
(1...1000000000000000000000000000000).sum
richiede tre aggiunte, una moltiplicazione, una sottrazione e una divisione.
È un numero costante di operazioni, ma la moltiplicazione è O ((log n) ²), quindi Enumerable#sum
è O ((log n) ²) per un intervallo intero.
iniettare
(1...1000000000000000000000000000000).inject(:+)
richiede 99999999999999999999999999999998 aggiunte!
L'aggiunta è O (log n), così Enumerable#inject
come O (n log n).
Con 1E30
come input, inject
con mai ritorno. Il sole esploderà molto prima!
Test
È facile controllare se vengono aggiunti Ruby Integer:
module AdditionInspector
def +(b)
puts "Calculating #{self}+#{b}"
super
end
end
class Integer
prepend AdditionInspector
end
puts (1..5).sum
#=> 15
puts (1..5).inject(:+)
# Calculating 1+2
# Calculating 3+3
# Calculating 6+4
# Calculating 10+5
#=> 15
Infatti, dai enum.c
commenti:
Enumerable#sum
Il metodo potrebbe non rispettare la ridefinizione del metodo di "+"
metodi come Integer#+
.