Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Problem: tablica n liczb całkowitych tab[n]=tab[0], tab[1], …, tab[n-1]; czy w tablicy tab występuje liczba x (podana jako parametr)? Rozumowanie: wziąć

Podobne prezentacje


Prezentacja na temat: "Problem: tablica n liczb całkowitych tab[n]=tab[0], tab[1], …, tab[n-1]; czy w tablicy tab występuje liczba x (podana jako parametr)? Rozumowanie: wziąć"— Zapis prezentacji:

1 Problem: tablica n liczb całkowitych tab[n]=tab[0], tab[1], …, tab[n-1]; czy w tablicy tab występuje liczba x (podana jako parametr)? Rozumowanie: wziąć pierwszy niezbadany element tablicy n-elementowej; jeśli aktualnie analizowany element tablicy jest równy x, to: wypisz Sukces i zakończ; w przeciwnym wypadku: zbadaj pozostałą część tablicy n-1 elementowej. Gdy przebadaliśmy całą tablicę i element nie został znaleziony, można np. wyświetlić komunikat o niepowodzeniu.

2 Przykładowa realizacja: int const n=10; int tab[n]={1,2,3,2,-7,44,5,1,0,-3}; void szukaj(int tab[n], int left, int right, int x) //left, right - lewa i prawa granica obszaru poszukiwań //tab - tablica //x - wartość do odnalezienia { if (left>right) cout << "Element " << x << " nie został odnaleziony\n"; else if (tab[left]==x) cout <<"Znalazłem szukany element "<< x <

3 Program ilustruje podstawowe cechy typowego programu rekurencyjnego: element znaleziony przekroczenie zakresu tablicy Zakończenie programu jest jasno określone z tablicy o rozmiarze n schodzimy do tablicy o rozmiarze n-1 Duży problem zostaje rozbity na problemy elementarne, które umiemy rozwiązać i na analogiczny problem, tylko o mniejszym stopniu skomplikowaniu

4 złe określenie warunku zakończenia programu niewłaściwa (nieefektywna) dekompozycja problemu

5 Przykład: Obliczanie silni unsigned long int silnia (int x) { if (x==0) return 1; else return x*silnia(x-1); } Jak się liczy 3! Proces przekazywania wyniku cząstkowego z poziomu niższego na wyższy Zagłębianie się programu z poziomu n na n-1 w celu dotarcia do przypadku elementarnego 0! Obliczanie wyników cząstkowych

6 Przykład: Ciąg Fibonacciego. (Elementy tego ciągu stanowią liczby naturalne takie, że kolejny wyraz (z wyjątkiem dwóch pierwszych) jest sumą dwóch poprzednich, tj. 1, 1, 2, 3, 5, 8, 13,…) unsigned long int Fibonaci(int n) { if(n<2) return n else return Fibonaci(n-1)+Fibonaci(n-2); } Obliczanie czwartego elementu ciągu Każde zacieniowane wyrażenie stanowi problem elementarny Znaczna część obliczeń jest wykonywana więcej niż jeden raz!!

7 Programy rekurencyjne są zazwyczaj dość pamięciożerne. Program obliczający 3! wywoła sam siebie tylko 3 razy, ale Fibonacci już nie jest taki łatwy do analizy. Przykład unsigned long int funkcja(int x) { if(x>100) return (x-10); else return funkcja(funkcja(x+11)); } Ile wywołań? Co się dzieje na większym przedziale liczbowym niż na rysunku? –ćw.

8 Stack overflow, czyli funkcja Ackermanna #include int A(int n,int p) { if (n==0) return 1; if ((p==0)&&(n>=1)) if (n==1)return 2; else return n+2; if ((p>=1)&&(n>=1)) return A(A(n-1,p),p-1); } int main() { cout << "A(3,4)="<

9 Stack overflow, czyli funkcja Ackermanna Analiza wywołań Pobieżna analiza funkcji A prowadzi do spostrzeżenia: Analogicznie dla 2 otrzymamy: Z samej definicji funkcji Ackermanna możemy wywnioskować, że Na bazie tych równań możliwe jest rekurencyjne udowodnienie, że

10 Stack overflow, czyli funkcja Ackermanna Analiza wywołań Nieco gorsza jest sytuacja dla A(n,4), bo trudno jest podać wzór ogólny. Ale można zobaczyć przykłady liczbowe:

11 Błąd programisty! Sprowokowanie nieskończonej ilości wywołań rekurencyjnych! Przykład: int niesk(int n) { if(n==1) return 1; else if ((n%2)==0) //czy n jest parzyste? return niesk(n-2)*n; else return niesk(n-1)*n; } Dla n>=2 wszystkie wywołania rekurencyjne kończą się parzystą liczbą n. Zatem dojdziemy do n=2, potem n=0, n=-2,…… Nigdzie po drodze nie ma przypadku elementarnego!

12 Błąd programisty! Jak go uniknąć? Sprawdzić matematycznie poprawność definicji rekurencyjnej, tzn. określić dziedziny wartości funkcji, udowodnić, że się ona zakończy, podać złożoność obliczeniową To nie wystarczy! Nie wiadomo, jak rzeczywisty kompilator wykona tę funkcję. int N(int n, int p) { if(n==0) return 1; else return N(n-1,N(n-p,p)); } Można udowodnić matematycznie, że powyższa definicja jest poprawna w tym sensie, że dla dowolnych n>=0, p>=0 jej wynik jest określony i wynosi 1. Zakłada się, że wartość argumentu wywołania funkcji jest obliczana tylko wtedy, gdy jest to konieczne. Jak wykona to typowy kompilator C++? Wszystkie parametry funkcji rekurencyjnej są obliczane jako pierwsze, a potem wywołana jest funkcja – wywołanie przez wartość.

13 int N(int n, int p) { if(n==0) return 1; else return N(n-1,N(n-p,p)); } Wszystkie parametry funkcji rekurencyjnej są obliczane jako pierwsze, a potem wywołana jest funkcja – wywołanie przez wartość. Zapętlenie jest spowodowane próbą obliczenia parametru p, tymczasem to drugie wywołanie nie jest potrzebne do zakończenia funkcji! Kompilator tego nie wie!

14 Łatwe do zrozumienia Zajmują mało miejsca (liczba wierszy kodu) – ewentualnie łatwo znaleźć błędy Jak w takim razie usunąć wady? Inaczej zbudować rekurencję. Rekurencja naturalna –poprzednie przykłady Rekurencja z parametrem dodatkowym Na czym polega?

15 unsigned long int silnia (int x) { if (x==0) return 1; else return x*silnia(x-1); } unsigned long int silnia2 (int x, int tmp=1) { if (x==0) return tmp; else return silnia2(x-1,x*tmp); } Parametry domyślne funkcji fun(int a, int k=1) Parametry domyślne funkcji fun(int a, int k=1) Funkcja może być wywołana na dwa sposoby: Poprzez określenie wartości drugiego parametru, np. fun(2,5), wtedy k przyjmuje wartość 5; Bez określania wartości drugiego parametru, np. fun(12), wtedy k przyjmuje wartość domyślną równą tej podanej w nagłówku, czyli 1. Parametr dodatkowy przekazuje elementy wyniku końcowego – program nie ma potrzeby przekazywania wyniku obliczeń do góry, piętro po piętrze – ostatni aktywny poziom dostarczy wynik!

16

17

18 Twierdzenie o rekurencji uniwersalnej podaje metodę rozwiązania tego typu rekurencji.

19

20

21 Zapis tego algorytmu w pseudokodzie:

22 Wywołania rekurencyjne Ćwiczenie: Zapisać w C++ Ćwiczenie: Zapisać w C++

23 Twierdzenie:

24

25

26 Komentarz:

27 Twierdzenie:

28

29 Gdy n jest dowolną liczbą naturalną rozwiązanie rekurencji jest trudniejsze. Należy wykorzystać następujący wzór sumacyjny: Twierdzenie:

30 Dowód:

31

32

33 Korzystając z podanego wcześniej wzoru sumacyjnego, możemy policzyć wartość sumy: co daje ostatecznie:


Pobierz ppt "Problem: tablica n liczb całkowitych tab[n]=tab[0], tab[1], …, tab[n-1]; czy w tablicy tab występuje liczba x (podana jako parametr)? Rozumowanie: wziąć"

Podobne prezentacje


Reklamy Google