ALGORYTMY I STRUKTURY DANYCH 1.Rekurencja
Transkrypt
ALGORYTMY I STRUKTURY DANYCH 1.Rekurencja
UNIWERSYTET EKONOMICZNY W KRAKOWIE KATEDRASYSTEMÓWOBLICZENIOWYCH ALGORYTMY I STRUKTURY DANYCH 1.Rekurencja Rekurencja inaczej rekursja (ang. recursion) to wywołanie z poziomu metody jej samej. Programowanie z wykorzytaniem rekurencji pozwala na przejrzyste zapisanie algorytmów, w których wynik końcowy jest złożeniem wielu wyników cząstkowych (np. wydajne algorytmy sortujące). Sprawdź co zwraca zapytanie: rekurencja w wyszukiwarce Google. Tworząc metody rekurencyjne musimy pamiętać o odpowiednim zakończeniu wywołań rekurencyjnych (nie mogą one odbywać się w nieskończoność). Zobaczmy poniższy kod źródłowy: public class Rekurencja { static void test() { System.out.println("Początek"); test(); System.out.println("Koniec"); } public static void main(String [] args) { test(); } } 1 Algorytmy i Struktury Danych Sposób realizacji programu: main(){ Początek Początek Początek Początek test() test() test() test() test() } Koniec Koniec Koniec Koniec itd. Efekt uruchomienia programu: UNIWERSYTET EKONOMICZNY W KRAKOWIE Początek Początek Początek Początek Początek Początek ........ Początek Exception in thread "main" java.lang.StackOverflowError Wywołania rekurencyjne muszą zakończyć się (w metodzie musi zostać zdefiniowany warunek zatrzymania się algorytmu, warunek stopu). Programzwarunkiemstopu Zmodyfikowana wersja programu zawierająca warunek stopu może wyglądać w następujący sposób: public class Rekurencja { static int licz = 0; static void test() { licz++; System.out.println("Początek"); if (licz <=5 ) test(); // warunek stopu System.out.println("Koniec"); } public static void main(String [] args) { test(); } } 2 Rekurencja Realizacja programu licz = 1 licz = 2 licz = 3 licz = 4 licz = 5 licz = 6 Początek Początek Początek Początek Początek Początek test() test() test() test() test() Koniec Koniec Koniec Koniec Koniec main(){ test() } Koniec UNIWERSYTET EKONOMICZNY W KRAKOWIE Efekt uruchomienia programu: Początek Początek Początek Początek Początek Początek Koniec Koniec Koniec Koniec Koniec Koniec Co się stanie jeśli jeszcze raz uruchomimy naszą metodę test()? Dlaczego tak się dzieje? Rekurencyjne obliczenie silni Wykorzystajmy naszą wiedzę do napisania prostego programu obliczającego wartość silni dla dowolnego n. public class Rekurencja { static int silnia(int n) { int pomoc; System.out.println("Wywołanie: silnia(" + n + ")"); if ((n == 0) || (n == 1)) pomoc = 1; else pomoc = n * silnia(n-1); System.out.println("Zwrócenie wyniku: " + n + "! = " + pomoc); return pomoc; } public static void main(String [] args) { System.out.println("4! = " + silnia(4)); } } 3 Algorytmy i Struktury Danych Sposób realizacji programu: main() { silnia(4); } 24 n = 4; pomoc = 4 * silnia(3); 6 n = 3; pomoc = 3 * silnia(2); 2 n = 2; pomoc = 2 * silnia(1); UNIWERSYTET EKONOMICZNY W KRAKOWIE 1 n = 1; pomoc = 1; Efekt działania programu: Wywołanie: silnia(4) Wywołanie: silnia(3) Wywołanie: silnia(2) Wywołanie: silnia(1) Zwrócenie wyniku: 1! Zwrócenie wyniku: 2! Zwrócenie wyniku: 3! Zwrócenie wyniku: 4! 4! = 24 = = = = 1 2 6 24 Uproszczona wersja programu (bez zmiennej pomoc, oraz wyświetlania poszczególnych etapów działania aplikacji) może wygladać tak: public class Rekurencja { static int silnia(int n) { if (n == 0) return 1; else return (n * silnia(n-1)); } public static void main(String [] args) { System.out.println("4! = " + silnia(4)); } } Powyższa wersja jest nieodporna na błędy. Co się stanie jeśli wywołamy metodę silnia() z parametrem ujemnym? 4 Rekurencja CiągFibonacciego Wykorzystanie rekurencji może okazać się bardzo kosztowne. Rozwiązania iteracyjne nieraz bywają bardziej wydajne. Zobaczmy jak wygląda definicja ciągu Fibonacciego. UNIWERSYTET EKONOMICZNY W KRAKOWIE Poniżej diagram przedstawia rekurencyjne obliczenie piątego elementu ciągu. fib(5) fib(4) fib(3) fib(3) fib(2) fib(1) fib(2) fib(2) fib(1) fib(1) fib(0) fib(1) fib(1) fib(0) fib(0) Zauważ ile razy algorytm oblicza te same dane, a jest to dopiero piąty element ciągu. Zadaniadowykonania 1. Napisz rekurencyjną metodę obliczającą sumę 1 + 2 + ... + n, gdzie wartość n jest parametrem funkcji. 2. Napisz rekurencyjną metodę obliczającą sumę cyfr w liczbie całkowitej podanej jako parametr. 3. Każdego roku pewna populacja królików podwaja się. Jeżeli początkowo było m królików to ile ich będzie po n latach? Napisz odpowiednią metodę rekurencyjną. 4. Napisz rekurencyjną metodę zwracającą podany jako parametr ciąg znaków w odwrotnej kolejności. 5 Algorytmy i Struktury Danych 5. Napisz w postaci rekurencyjnej metodę wyznaczającą, za pomocą algorytmu Euklidesa, największy wspólny dzielnik dwóch liczb całkowitych dodatnich . 6. Napisz z wykorzystaniem rekurencji metodę potęga obliczającą n-tą potęgę liczby m. np.: wywołanie: potega(3, 1) zwróci wynik: 3 wywołanie: potega(3, 2) zwróci wynik: 9 wywołanie: potega(3, 3) zwróci wynik: 27 7. Napisz metodę wyznaczającą rekurencyjnie n-ty element ciągu Fibonacciego. Po wyznaczeniu n-tego elementu wyświetl ile razy metoda została wywołana. Zastanów się i przedstaw również rozwiązanie iteracyjne oraz sprawdź za pomocą klasy Stoper czasy wykonania obu metod. Klasa Stoper jest dostępna w zadaniach z przedmiotu Programowanie Komputerów. UNIWERSYTET EKONOMICZNY W KRAKOWIE 8. Napisz metodę wyznaczającą binarną reprezentację podanej jako parametr liczby całkowitej. Zastosuj rozwiązanie rekurencyjne. Jak wiadomo wartość binarna wyznaczana jest poprzez odczytanie od tyłu reszt z dzielenia przez 2, a właściwy porządek cyfr można osiągnąć poprzez odpowiednie zastosowanie rekurencji. 6