Ответ 1
Короткий ответ
Для целочисленного диапазона:
-
Enumerable#sum
возвращает(range.max-range.min+1)*(range.max+range.min)/2
-
Enumerable#inject(:+)
выполняет итерацию по каждому элементу.
Теория
Сумма целых чисел от 1 до n
называется треугольным числом и равна n*(n+1)/2
.
Сумма целых чисел между n
и m
является треугольным числом m
минус треугольное число n-1
, которое равно m*(m+1)/2-n*(n-1)/2
и может быть записано (m-n+1)*(m+n)/2
.
Перечислимая сумма в Ruby 2.4
Это свойство используется в Enumerable#sum
для целых диапазонов:
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
выглядит следующим образом:
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);
что эквивалентно:
(range.max-range.min+1)*(range.max+range.min)/2
вышеупомянутое равенство!
Сложность
Большое спасибо @k_g и @Hynek-Pichi-Vychodil за эту часть!
сумма
(1...1000000000000000000000000000000).sum
требует трех дополнений, умножения, вычитания и деления.
Это постоянное количество операций, но умножение - O ((log n) ²), поэтому Enumerable#sum
- O ((log n) ²) для целочисленного диапазона.
инъекционные
(1...1000000000000000000000000000000).inject(:+)
требуется 999999999999999999999999999998 дополнений!
Дополнение - O (log n), поэтому Enumerable#inject
- O (n log n).
С 1E30
в качестве входных данных inject
, никогда не возвращающийся. Солнце рано взорвется!
Тест
Легко проверить, добавляются ли 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
В самом деле, из enum.c
комментариев:
Enumerable#sum
метод может не учитывать переопределение метода"+"
методы, такие какInteger#+
.