Réponse courte
Pour une plage entière:
Enumerable#sum
Retour (range.max-range.min+1)*(range.max+range.min)/2
Enumerable#inject(:+)
itère sur chaque élément.
Théorie
La somme des nombres entiers compris entre 1 et n
est appelée un nombre triangulaire et est égale à n*(n+1)/2
.
La somme des entiers entre n
et m
est le nombre triangulaire de m
moins le nombre triangulaire de n-1
, qui est égal à m*(m+1)/2-n*(n-1)/2
, et peut être écrit (m-n+1)*(m+n)/2
.
# Somme énumérable dans Ruby 2.4
Cette propriété est utilisée Enumerable#sum
pour les plages d'entiers:
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
ressemble à ça :
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);
ce qui équivaut à:
(range.max-range.min+1)*(range.max+range.min)/2
l'égalité mentionnée ci-dessus!
Complexité
Merci beaucoup à @k_g et @ Hynek-Pichi-Vychodil pour cette partie!
somme
(1...1000000000000000000000000000000).sum
nécessite trois ajouts, une multiplication, une soustraction et une division.
C'est un nombre constant d'opérations, mais la multiplication est O ((log n) ²), donc Enumerable#sum
comme O ((log n) ²) pour une plage d'entiers.
injecter
(1...1000000000000000000000000000000).inject(:+)
nécessite 999999999999999999999999999998 ajouts!
L'addition est O (log n), tout Enumerable#inject
comme O (n log n).
Avec 1E30
comme entrée, inject
avec jamais de retour. Le soleil va exploser bien avant!
Tester
Il est facile de vérifier si des entiers Ruby sont ajoutés:
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
En effet, d'après les enum.c
commentaires:
Enumerable#sum
method peut ne pas respecter la redéfinition de méthode de "+"
méthodes telles que Integer#+
.
n+1
uniquement les gammes? Je n'ai pas installé 2.4 ou je me testerais moi-même, mais d'autres objets énumérables sont traités par ajout de base, car ils seraient eninject(:+)
moins la surcharge du symbole à proc.n, n+1, n+2, .., m
constituent une série arithmétique dont la somme est égale(m-n+1)*(m+n)/2
. De même, la somme d'une série géométrique ,n, (α^1)n, (α^2)n, (α^3)n, ... , (α^m)n
. peut être calculé à partir d'une expression de forme fermée.