Fibonacci z zapamiętywaniem

Zadanie 11 z 13 · rozdział 15Trudność: 2 z 3rekurencjaFibonaccimemoizacja

Treść zadania

Oblicz $F_N$ — $N$-ty wyraz ciągu Fibonacciego ($F_0 = 0$, $F_1 = 1$, $F_n = F_{n-1} + F_{n-2}$), ale tym razem dla $N$ aż do $90$. Prosta rekurencja z ZAD-05 nie zdąży: dla $N = 90$ wykonałaby około $10^{19}$ wywołań.

Napisz rekurencyjną funkcję fibonacci(n, pamiec), która zapamiętuje obliczone wyniki (memoizacja): pamiec to lista, w której pamiec[k] jest równe None, dopóki $F_k$ nie zostało obliczone, a potem przechowuje jego wartość. Każdy wyraz ciągu jest wtedy liczony tylko raz.

Dane wejściowe

Jedna liczba naturalna N.

Dane wyjściowe

Jedna liczba naturalna — wartość $F_N$.

Ograniczenia

  • 0 ≤ N ≤ 90

Uwagi

  • Funkcja: jeśli $n < 2$, zwróć $n$. Jeśli pamiec[n] nie jest None, zwróć zapamiętaną wartość. W przeciwnym razie oblicz fibonacci(n - 1, pamiec) + fibonacci(n - 2, pamiec), zapisz wynik w pamiec[n] i zwróć go.
  • Porównanie liczby wywołań: wersja bez pamięci liczy te same wyrazy wielokrotnie — dla $N$ wykonuje $2F_{N+1} - 1$ wywołań, czyli liczba wywołań rośnie wykładniczo (dla $N = 40$ to już ponad 300 milionów). Wersja z pamięcią oblicza każdy wyraz raz, więc wykonuje mniej niż $2N$ wywołań — liczba wywołań rośnie liniowo.
  • Dla chętnych: Python ma gotowy mechanizm zapamiętywania. Dekorator @lru_cache z modułu functools, dopisany nad definicją funkcji, sam zapamiętuje wyniki dla każdego argumentu:
    ```python
    from functools import lru_cache
    @lru_cache(maxsize=None)
    def fib(n):
    if n < 2:
    return n
    return fib(n - 1) + fib(n - 2)
    `

Przykład

Wejście
40
Wyjście
102334155
Zasady obowiązujące w rozdziale 15

Poniższe zadania uczą rekurencji: funkcja rozwiązuje problem, wywołując samą siebie dla mniejszych danych, aż dojdzie do przypadku bazowego, w którym odpowiedź jest znana od razu (np. $0! = 1$).

Konwencje wspólne:

  • Każde zadanie to osobny program: czyta standardowe wejście i wypisuje wynik na standardowe wyjście. Program wczytuje dane, wywołuje funkcję opisaną w treści i wypisuje wynik (gotowy szkielet znajdziesz w sekcji „Kod startowy”).
  • Rozwiązanie musi być rekurencyjne. Właściwe obliczenia wykonuje funkcja, która wywołuje samą siebie. Nie używaj w niej pętli for ani while, ani gotowych narzędzi, które wykonają całą pracę za Ciebie (np. sum(), pow(), operatora **, math.factorial(), list.index()). Sprawdzarka porównuje tylko wynik, ale celem zadań jest ćwiczenie rekurencji.
  • Każda funkcja rekurencyjna potrzebuje przypadku bazowego (kiedy przestajemy się wywoływać) i kroku rekurencyjnego (wywołania dla mniejszego problemu). Bez przypadku bazowego program przerwie działanie błędem RecursionError.
  • Python ogranicza głębokość rekurencji (domyślnie do około 1000 zagnieżdżonych wywołań), dlatego dane w zadaniach są małe.
  • Liczby naturalne liczymy od zera: $0, 1, 2, \dots$
  • Program nie wypisuje komunikatów typu „Podaj liczbę:”.

Zadanie pochodzi z otwartego zbioru Nauka-Programowania (z rozwiązaniami wzorcowymi). Zgłoś błąd w treści lub testach.

Python uruchomi się w przeglądarce przy pierwszym teście.

Kod zapisuje się automatycznie w tej przeglądarce. Tab wstawia wcięcie; aby opuścić edytor klawiaturą, naciśnij Esc, a potem Tab.

Testy

Program dostaje „Wejście” przez input() i musi wypisać „Oczekiwane wyjście”. Liczby porównywane są z tolerancją 0,01, a tekst podany w input("…") nie jest sprawdzany.

Test 1

Nie uruchomiono
Wejście
0
Oczekiwane wyjście
0

Test 2

Nie uruchomiono
Wejście
1
Oczekiwane wyjście
1

Test 3

Nie uruchomiono
Wejście
2
Oczekiwane wyjście
1

Test 4

Nie uruchomiono
Wejście
50
Oczekiwane wyjście
12586269025

Test 5

Nie uruchomiono
Wejście
70
Oczekiwane wyjście
190392490709135

Test 6

Nie uruchomiono
Wejście
90
Oczekiwane wyjście
2880067194370816120
Uruchom z własnymi danymi