Я знаю, что этот вопрос хорошо обсуждается, но я пришел к делу. Я действительно не понимаю, как рекурсивный метод "медленнее", чем метод, использующий "уменьшить, лямбда, xrange".
def factorial2(x, rest=1):
if x <= 1:
return rest
else:
return factorial2(x-1, rest*x)
def factorial3(x):
if x <= 1:
return 1
return reduce(lambda a, b: a*b, xrange(1, x+1))
Я знаю, что python не оптимизирует хвостовую рекурсию, поэтому вопрос не в этом. По моему мнению, генератор должен генерировать n количество чисел, используя оператор +1
. Технически, fact(n)
должно добавить число n
раз, как рекурсивное. lambda
в reduce
будет называться n
раз так же, как рекурсивный метод... Поэтому, поскольку в обоих случаях у нас нет оптимизации хвостовых вызовов, стеки будут созданы/уничтожены и возвращены n
раз, А if
в генераторе должен проверять, когда поднимать исключение StopIteration
.
Это заставляет меня задаться вопросом, почему рекурсивный метод все еще медленнее, чем другой, поскольку рекурсивный использует простую арифметику и не использует генераторы.
В тесте я заменил rest*x
на x
на рекурсивный метод, а потраченное время было уменьшено наравне с методом с использованием reduce
.
Вот мои тайминги для факта (400), 1000 раз
factorial3: 1.22370505333
factorial2: 1.79896998405
Edit:
Запуск метода от 1
до n
не помогает вместо n
до 1
. Так что не накладные расходы с -1
.
Кроме того, мы можем сделать рекурсивный метод быстрее. Я пробовал несколько вещей, таких как глобальные переменные, которые я могу изменить... Использование изменяемого контекста путем размещения переменных в ячейках, которые я могу изменить как массив, и сохранить рекурсивный метод без параметров. Отправка метода, используемого для рекурсии, в качестве параметра, поэтому нам не нужно "разыгрывать" его в нашей области...?! Но ничего не делает это быстрее.
Я укажу, что у меня есть версия того, что я использую forloop, который намного быстрее, чем оба этих метода, поэтому есть явное пространство для улучшения, но я не ожидал ничего быстрее, чем forloop.