rekurencja 1
Rekurencja/rekursja Alternatywny dla pętli sposób powtarzania pewnych czynności; kolejny etap podzadanie poprzedniego Rekursja może być zamieniona na iteracje Cechy rekurencji Rozłożenie problemu na problemy elementarne i na problem mniej skomplikowany niż wejściowy Jasno określony warunek zakończenia (przypadek podstawowy, zdegenerowany) Iluzja istnienia wielu kopii tego samego algorytmu (aktywacji) Tylko jedna aktywacja jest aktywna w danej chwili 2
silnia function silnia(x) if (x=0) then (silnia:=1; return) ( silnia:=x*silnia(x-1)) silnia(4) 4*silnia(3) silnia(3) 3*silnia(2) silnia(2) 2*silnia(1) silnia(1) 1*silnia(0) silnia(0)=1 procedure silnia(x) wynik:=1; i:=1; repeat (wynik:=wynik *i; i:=i+1) until (i>x) procedure silnia(x) wynik:=1; i:=1; while (i<=x) do (wynik:=wynik *i; i:=i+1) 3
Wyszukiwanie binarne l i i=1,..n procedure szukaj(l,p,k,wartosc) if((k-p)<0) then (return porażka) j:=(p+k)/2; if (wartość=l j ) then (return sukces - j ) if (wartość >l j ) then szukaj(l,j+1,k,wartosc) szukaj(l,p,j-1, wartosc) 4
Krzywe Hilberta H 1-4 puste krzywe H 0 połączone liniami prostymi H 2-4 kopie H 1 zmniejszone i obrócone połączone liniami prostymi 4 warianty: A: D A A B A D B: C B B A C: B C C D A B D: A D D C 5
Krzywe Sierpińskiego A D B C S 2 S 1 S 3 A: A BD A S 1 S 2 S 3 S 4 B: B C A B S: A B C D C: C D <= B C D: D A C D 6
Wieże Hanoi X Y Z procedure move(n,x,y,z) if (n=1) then (X Y) (move(n-1,x,z,y); X Y; move(n-1,z,y,x)) X Y Z 3,X,Y,Z 2,X,Z,Y X Y 2,Z,Y,X 1,X,Y,Z X Z 1,Y,Z,X 1,Z,X,Y Z Y 1,X,Y,Z X Y Y Z Z X X Y 7
Wieże Hanoi X Y Z X Y Z procedure move(n,x,y,z) repeat (przenies najmniejszy krazek na nastepny kolek; przenies nienajmniejszy krazek (jedyny ruch)) until(ok) X Y X Z Y Z X Y Z X Z Y X Y 8
Wieże Hanoi Liczba pojedynczych przeniesień krążka= 2 N -1 N liczba krążków Złożoność wykładnicza Problem Tybetańczyków 64 krążki Przeniesienie 1 krążka 10 s 5 bilionów (10 12 ) lat 9
Algorytmy z powrotami Droga skoczka szachowego Problem ośmiu hetmanów (Gauss 1850; 12 różnych istotnie rozwiązań) Problem trwałego małżeństwa 10
Droga skoczka szachowego procedure proba repeat (wybierz ruch; if (dopuszczalny) then (zapisz go;) if (sa puste pola) then (proba; if(nieudany) then (wykresl ostatni zapis) udany)) until (ruch udany lub brak ruchu) 11
Niebezpieczeństwa rekurencji Powtarzanie części obliczeń (ciąg Fibonacciego) programowanie dynamiczne Przepełnienie stosu Błąd nieskończona liczba wywołań Różne kompilatory różne wyniki identycznych programów 12
Ciąg Fibonacciego fib(0)=0 fib(1)=1 fib(n)=fib(n-1) + fib(n-2) dla n>=2 fib(0) fib(2) function fib(x) if(x=0) then (fib:=0) if(x=1) then (fib:=1) fib:=fib(x-1)+fib(x-2) fib(4) fib(3) fib(1) fib(1) fib(2) fib(0) fib(1) 13
Przepełnienie stosu procedure MacCarthy(x) if (x>100) then (wynik:=x-10) (wynik:=maccarthy(maccarthy(x+11))) MC(96)---MC(MC(107))----MC(97)----MC(MC(108))----MC(98)----- MC(MC(109))----MC(99)----MC(MC(110))----MC(100)--- MC(MC(111))----MC(101)----91 14
nieskończona liczba wywołań Upraszczanie nie doprowadza do przypadku elementarnego function fun(n) if(n=1) then return 1 (if (n%2 =0) then ) (fun:=fun(n-2)*n; return fun) (fun:=fun(n-1)*n; return fun) 15
Nieskończony ciąg wywołań function fun(n,m) if (n=0) then return 1 return fun(n-1,fun(n-m,m) fun(1,0) fun(0,fun(1,0)) fun(0,fun(1,0)) fun(0,fun(1,0)). Parametry funkcji rekurencyjnej wartościowane jako pierwsze 16
Przykłady funkcji rekurencyjnych w języku C 17
silnia double silnia(int x) if(x<=0) return 1; return (x*silnia(x-1)); 18
Zamiana na liczbę binarną void zamiana(int liczba) if(liczba>1) zamiana(liczba/2); printf("%d",liczba%2); printf("%d\n",liczba); 19
Binarne wyszukiwanie int szukaj(int p, int k, int *tab, int x) int s; if(k==p) if(tab[p]==x) s=(p+k)/2; return p; return -1; if(tab[s]==x) return s; if(x>tab[s]) return szukaj(s+1,k,tab,x); return szukaj(p,s,tab,x); 20
Odwracanie posortowanej tablicy void odw(tab,p,k) int p,k; int *tab; int pom; if(p<k) pom=tab[p]; tab[p]=tab[k]; tab[k]=pom; odw(tab,p+1,k-1); 21
Zliczanie ostatnich 0 w reprezentacji binarnej liczby całkowitej int licz0(int k) if(k==0) return 1; if (k%2) return 0; return 1+licz0(k/2); 22
Zliczanie ostatnich 1 w reprezentacji binarnej liczby całkowitej int licz1(int k) if (k%2) return 1+licz1(k/2); return 0; 23
Algorytm Euklidesa int nwd_euklides(int m, int n) if(n==0 )return m; return nwd_euklides(n, m%n); 1. Przypisz zmiennym M i N odpowiednio większą i mniejszą wartość wejściową 2. Podziel M przez N, resztę z dzielenia nazwij R 3. Jeśli R nie jest zerem, to przypisz zmiennej M wartość N, zmiennej N wartość R i powróć do kroku 2, w przeciwnym razie NWD to wartość N 24
Szukanie wartości minimalnej w tablicy int mint( n, t, i) int n, t[n],i; int kk; if(i==n-1) return t[i]; kk = mint(n,t,i+1); if ( t[i] < kk ) return t[i]; return kk; 25
Wyszukiwanie binarne maksymalny element tablicy int max(a,p,k) int *a,p,k; int s,max1,max2; if(p==k) return a[p]; s=(p+k)/2; max1=max(a,p,s); max2=max(a,s+1,k); if (max1>max2) return max1; return max2; 26
Wyszukiwanie binarne - strategia dziel i rządź Dwa wywołania rekurencyjne Każde obsługuje połowę zbioru danych wejściowych 27
Metoda dziel i zwyciężaj Podział problemu na kilka mniejszych, podobnych do początkowego Rozwiązywanie rekurencyjne podproblemów Łączenie rozwiązań Etapy każdego poziomu rekursji Dziel Zwyciężaj rozwiązanie małych podproblemów rekurencyjnie, dla małych rozmiarów metody bezpośrednie Połącz Przykład sortowanie przez scalanie 28
Programowanie dynamiczne Poprawia metodę dziel i zwyciężaj w sytuacji wielokrotnego rozwiązywania tych samych problemów Czas wykładniczy liniowy Wstępujące Obliczenie wartości począwszy od najmniejszych wartości; Obliczenie bieżącej wartości korzystając z poprzednio obliczonych wartości Warunek istnieje możliwość gromadzenia obliczonych wartości Zstępujące (zapamiętywanie) Procedura rekurencyjna sprawdza zapisane wartości i zapisuje obliczoną przez siebie wartość 29
Liczby Fibonacciego int F(int i) if(i<1) return 0; if(i==1) return 1; return F(i-1)+F(i-2); int main() int k; cin>>k; cout<<"f("<<k<<")="<<f(k); const int maxn=100; int F(int i) static int Fib[maxN]; if(i<0) return 0; if(fib[i]!=0) return Fib[i]; int t=i; if(i>1) t=f(i-1)+f(i-2); return Fib[i]=t; int main() int k; cin>>k; cout<<"f("<<k<<")="<<f(k); 30
Liczby Fibonacciego Program1 czas wykładniczy T( n) a b n O( ), 1. 618, złoty podział odcinka a b a a b Np. obliczenie F(n) 1 s obliczenie F(n+9) 1min obliczenie F(n+18) 1h Program2 - wykorzystuje tablicę statyczną (na początku wypełniona 0) T(n)=O(n) F(45)=1 836 311 903 - największa 32-bitowa całkowita liczba Fibonacciego -potrzebna tablica 46-elementowa 9 60 31
Liczby Fibonacciego F6 2 1 1 3 1 4 5 Program1 25 wywołań 3 2 2 1 1 1 1 1 6 1 0 3 4 2 1 1 1 1 1 1 2 3 1 2 4 2 5 3 6 5 8 Program2 6 wywołań 0 0 1 1 2 1 3 2 4 3 5 5 6 8 7 15 wypełnienie tablicy 32
Symbol Newtona- rekurencja n m m m n n m m m n m n m n 0 1 0, 1 1 1 dla dla int symbol_newtona(int n, int m) int sn; if(m==0 m==n) return 1; sn=symbol_newtona(n-1,m)+symbol_newtona(n-1,m-1); return sn; ) ( ) ( n O a n T sn(4,2)= sn(3,2) + sn(3,1) = sn(2,2)+sn(2,1)+sn(2,1)+sn(2,0)= 1+sn(1,1)+sn(1,0)+sn(1,1)+sn(1,0) +1=6 33
Symbol Newtona programowanie dynamiczne int sn(int n, int m) static int tab[maxn]; int i,j; if(m==0 m==n) return 1; for(j=0;j<=n;j++) tab[j]=1; for(i=2;i<=n;i++) for(j=i-1;j>0;j--)//j>m-1 tab[j]=tab[j-1]+tab[j]; return tab[m]; i w i-tym kroku tab[j] = j T( n) O( n 2 ) 34
Symbol Newtona programowanie dynamiczne w i-tym kroku tab[j] = tab[0] 1 i j n=6 i=1 i=2 i=3 i=4 i=5 i=6 tab[1] 1 2 3 4 5 6 tab[2] 1 3 6 10 15 tab[3] 1 4 10 20 tab[4] 1 5 15 tab[5] 1 6 4 dla i=4 tab[3] = = 4 3 6 dla i=6 tab[3] = = 20 3 n m 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 n n>m n = 0, 1, m m = 0, 1,. 4 5 3 35 2