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