Wiem, że ten temat jest dobrze omawiany, ale doszedłem do sprawy, której nie rozumiem, jak metoda rekurencyjna jest "wolniejsza" niż metoda wykorzystująca "zmniejszyć, lambda, xrange".Powolna rekursja w pytonie
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))
Wiem, że Python nie optymalizuje rekursji ogon, więc pytanie nie jest o tym. Według mojej wiedzy generator powinien generować n liczby liczb za pomocą operatora +1
. Więc technicznie, fact(n)
powinien dodać numer n
razy tak jak rekursywny. lambda
w reduce
będzie nazywać się n
razy tak samo jak metoda rekursywna ... Tak więc, ponieważ nie mamy optymalizacji wywołań końcowych w obu przypadkach, stosy będą tworzone/niszczone i zwracane n
razy. I if
w generatorze powinien sprawdzić, kiedy podnieść wyjątek StopIteration
.
Zastanawiam się, dlaczego metoda rekursywna jest nadal wolniejsza od drugiej, ponieważ rekursywna używa prostej arytmetyki i nie używa generatorów.
W teście zastąpiłem rest*x
przez x
w metodzie rekurencyjnej, a czas spędzony został zmniejszony na równi z metodą z użyciem reduce
.
Oto moje czasy dla faktu (400), 1000 razy
factorial3: +1,22370505333
factorial2: 1,79896998405
Edit:
Making metody start od 1
do n
nie pomaga eit jej zamiast n
do 1
. Więc nie narzut z -1
.
Co więcej, możemy przyspieszyć metodę rekurencyjną. Próbowałem wielu rzeczy, takich jak zmienne globalne, które mogę zmienić ... Używając kontekstu zmiennego, umieszczając zmienne w komórkach, które mogę modyfikować jak tablicę i zachowuję metodę rekursywną bez parametrów. Wysyłanie metody używanej do rekursji jako parametru, więc nie musimy "dereferencji" w naszym zakresie ...?! Ale nic nie sprawia, że jest to szybsze.
Podkreślę, że mam wersję faktu, że użycie forloop jest znacznie szybsze niż obie z tych dwóch metod, więc jest wyraźnie miejsce na ulepszenia, ale nie oczekiwałbym niczego szybciej niż forloop.
mam wrażenie, że jest to 'range' być zoptymalizowanym. Zastąpienie 'range/xrange' przez mój własny generator znacznie pogarsza sytuację. Możliwe, że 'zasięg' jest zaimplementowany w' c', a nie w pythonie, co tłumaczy dlaczego metoda rekursywna nie może pokonać żadnej metody używającej 'range'. Również wyniki mogą się znacznie różnić przy użyciu innego interpretera niż ... jak Pypy. –
O ile gorszy jest twój własny generator? Testowałem to również i choć było gorzej niż "xrange" Pythona, wciąż było znacznie lepiej niż rozwiązanie rekursywne. –
Podczas gdy różnica w taktowaniu jest prawdopodobnie znacząca, dwa środowiska wykonawcze są nadal w tym samym rzędzie wielkości. Tak więc 'range' /' xrange' robiący dodawanie/iterowanie w C w przeciwieństwie do 'factorial2' lub własny generator robiący to w pythonie może wyjaśnić różnicę. Dodatkowo bez rekursji ogonowej 'factorial2 'musi zbudować rekurencyjny pakiet wywołań (co oznacza iteracyjne przydzielanie pamięci), a każda iteracja ma gałąź, która może zranić optymalizacje, takie jak potokowanie. –