Wiesz już, jak podchodzić do rozwiązywania stawianych przed tobą problemów programistycznych. Schemat wygląda tak, że zwykle najpierw próbujemy skorzystać z gotowego rozwiązania, a jeśli to nie zadziała, modyfikujemy i rozszerzamy rozwiązania, które znamy. Ostatecznie sięgamy po metodę łączenia rozwiązań częściowych. Próbujemy zatem uprościć proces. Są jednak problemy, które możemy rozwiązać, rozbijając je na takie podproblemy, które są podobne do całości. Rozwiązywanie mniejszych podproblemów ma zaś doprowadzić do rozwiązania problemu głównego. Taką metodę nazywamy metodą rekurencyjną.

Definicja rekurencji

Z rekurencją w informatyce spotykamy się w sytuacji, gdy korzystamy z rozwiązań tego samego problemu, ale dla mniejszej wartości lub liczby danych. W takim wypadku często stosujemy algorytm rekurencyjny, czyli odwołujący się do siebie. Algorytm kończy się, gdy potrafimy podać odpowiedź bez korzystania z rozwiązań dla mniejszych danych.

Przejdźmy do matematycznej definicji ciągu zdefiniowanego rekurencyjnie. Najpierw zapisujemy wyraz ciągu o indeksie 0 (lub kilka początkowych jego wyrazów). Następnie podajemy wzór na wyraz o indeksie n wyrażony za pomocą wyrazów poprzednich.

Wzór rekurencyjny uzależnia więc wartość dowolnego (ogólnego) wyrazu tego ciągu od wartości poprzedzających go wyrazów.

Przykład 1

Zapiszmy za pomocą pseudokodu algorytm jedzenia kaszki, który przedstawiliśmy w sekcji „Na dobry początek”.

Linia 1. Jedz kaszkę. Linia 2. jeśli talerz jest pusty. Linia 3. koniec jedzenia. Linia 4. w przeciwnym razie. Linia 5. zjedz łyżkę kaszki. Linia 6. Jedz kaszkę.

By lepiej zobrazować działanie algorytmu, zapiszmy go tak:

Linia 1. Jedz kaszkę. Linia 2. jeśli talerz jest pusty. Linia 3. koniec jedzenia. Linia 4. w przeciwnym razie. Linia 5. zjedz łyżkę kaszki. Linia 6. Jedz kaszkę. Linia 7. jeśli talerz jest pusty. Linia 8. koniec jedzenia. Linia 9. w przeciwnym razie. Linia 10. zjedz łyżkę kaszki. Linia 11. Jedz kaszkę. Linia 12. kropka kropka kropka.

Zauważyć można, że sekwencja się powtarza – jedzenie kaszki (w programie byłaby to funkcja) powtarza się w ramach jedzenia kaszki (czyli funkcja wywołuje funkcję).

Przykład 2

Zapoznajmy się z przykładem matematycznym.

Jak wyznaczyć wyraz ciągu o indeksie z ciągu zdefiniowanego rekurencyjnie?

Oto definicja pewnego ciągu określonego rekurencyjnie. Wyraz ciągu o indeksie 0 jest podany, np.:

a n = { 2 d l a a n 1 + 3 d l a n = 0 n > 0

W jaki sposób możemy wyznaczyć wyraz ciągu o indeksie n? Przeanalizuj algorytm zapisany za pomocą pseudokodu wykorzystujący funkcję rekurencyjną.

Specyfikacja problemu:

Dane

  • n – liczba całkowita; indeks wyrazu w ciągu, który chcemy obliczyć

Wynik

  • obliczony wyraz ciągu o indeksie n

Linia 1. funkcja rekurencja otwórz nawias okrągły n zamknij nawias okrągły dwukropek. Linia 2. jeżeli n znak równości 0 dwukropek. Linia 3. zwróć 2. Linia 4. w przeciwnym razie dwukropek. Linia 5. zwróć rekurencja otwórz nawias okrągły n minus 1 zamknij nawias okrągły plus 3.

Największą zaletą funkcji rekurencyjnych jest prostota ich zapisu. Algorytm jest krótki i zrozumiały.

Implementacja pseudokodu w języku Pythhon

Linia 1. def rekurencja otwórz nawias okrągły n zamknij nawias okrągły dwukropek. Linia 2. if n znak równości znak równości 0 dwukropek. Linia 3. return 2. Linia 4. else dwukropek. Linia 5. return rekurencja otwórz nawias okrągły n minus 1 zamknij nawias okrągły plus 3. Linia 6. print otwórz nawias okrągły rekurencja otwórz nawias okrągły 5 zamknij nawias okrągły zamknij nawias okrągły. Linia 7. kratka wynik. Linia 8. kratka 17.

W przykładzie została opisana za pomocą pseudokodu funkcja rekurencyjna, która oblicza n-ty wyraz ciągu według podanego wzoru rekurencyjnego. Jej parametrem jest indeks wyrazu ciągu, który chcemy obliczyć.

Jeżeli argumentem funkcji będzie wartość 0, to zwróconą wartością będzie 2. Jeśli nie, ponownie wywoływana jest funkcja rekurencyjna, ale z inną wartością argumentu. W tym przypadku wartością argumentu jest indeks poprzedniego wyrazu ciągu, czyli n - 1.

Algorytm jest rekurencyjny wtedy, gdy do rozwiązania problemu wykorzystuje on sam siebie. Funkcja jest rekurencyjna wtedy, gdy wywołuje samą siebie. Kolejne wywołania takiej funkcji nazywamy rekurencyjnym ciągiem wywołań.

Rekurencyjny ciąg wywołań musi być skończony, stąd w rekurencji musi być określony warunek kończący rekurencję.

Proces taki możemy pokazać za pomocą drzewa wywołań rekurencyjnych. Przedstawiony na ilustracji przykład jest drzewem wywołań rekurencyjnych dla początkowego wywołania funkcji a(5). Takie wywołanie zwróci wartość 17.

R773dixte7pUY
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.

Słownik

iteracja
iteracja

technika programowania polegająca na powtarzaniu tych samych operacji w pętli określoną liczbę razy lub do momentu, aż zostanie spełniony zadany warunek

NWD
NWD

największy wspólny dzielnik dwóch liczb – największa liczba naturalna, która dzieli obie te liczby bez reszty

przepełnienie pamięci
przepełnienie pamięci

błąd polegający na przekroczeniu rozmiaru zarezerwowanego miejsca dla programu w pamięci operacyjnej

rekurencja
rekurencja

technika programowania polegająca na odwoływaniu się procedur lub funkcji do samych siebie, aż do momentu spełnienia warunku podstawowego

stos
stos

dynamiczna struktura danych, w której informacje pobierane są ze szczytu i na niego odkładane; struktura typu LIFO (Last‑In, First‑Out – ostatni na wejściu, pierwszy na wyjściu)