Respuesta corta
Para un rango entero:
Enumerable#sum
devoluciones (range.max-range.min+1)*(range.max+range.min)/2
Enumerable#inject(:+)
itera sobre cada elemento.
Teoría
La suma de los enteros entre 1 y n
se llama un número triangular , y es igual a n*(n+1)/2
.
La suma de los enteros entre n
y m
es el número triangular de m
menos el número triangular de n-1
, que es igual a m*(m+1)/2-n*(n-1)/2
, y se puede escribir (m-n+1)*(m+n)/2
.
Enumerable # sum en Ruby 2.4
Esta propiedad se utiliza en Enumerable#sum
rangos enteros:
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
Se ve como esto :
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);
que es equivalente a:
(range.max-range.min+1)*(range.max+range.min)/2
¡La mencionada igualdad!
Complejidad
¡Muchas gracias a @k_g y @ Hynek-Pichi-Vychodil por esta parte!
suma
(1...1000000000000000000000000000000).sum
requiere tres adiciones, una multiplicación, una resta y una división.
Es un número constante de operaciones, pero la multiplicación es O ((log n) ²), también lo Enumerable#sum
es O ((log n) ²) para un rango entero.
inyectar
(1...1000000000000000000000000000000).inject(:+)
requiere 99999999999999999999999999999998 adiciones!
La suma es O (log n), también lo Enumerable#inject
es O (n log n).
Con 1E30
como entrada, inject
con nunca retorno. ¡El sol explotará mucho antes!
Prueba
Es fácil verificar si se están agregando Ruby Integers:
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
De hecho, de los enum.c
comentarios:
Enumerable#sum
El método puede no respetar la redefinición de "+"
métodos como Integer#+
.
n+1
solo para rangos? No tengo 2.4 instalado o me probaría a mí mismo, pero hay otros objetos enumerables manejados por adición básica, ya que estarían eninject(:+)
menos la sobrecarga del símbolo para el proceso.n, n+1, n+2, .., m
constituyen una serie aritmética cuya suma es igual(m-n+1)*(m+n)/2
. Del mismo modo, la suma de una serie geométrica ,n, (α^1)n, (α^2)n, (α^3)n, ... , (α^m)n
. se puede calcular a partir de una expresión de forma cerrada.