Rekurencja (rekursja)



Podobne dokumenty
Podstawy programowania. Wykład: 13. Rekurencja. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

ALGORYTMY I STRUKTURY DANYCH

Podstawy programowania 2. Temat: Funkcje i procedury rekurencyjne. Przygotował: mgr inż. Tomasz Michno

Rekurencja. Rekurencja zwana także rekursją jest jedną z najważniejszych metod konstruowania rozwiązań i algorytmów.

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Rekurencja. Przykład. Rozważmy ciąg

Programowanie komputerowe. Zajęcia 3

Programowanie - wykład 4

Funkcje i tablice. Elwira Wachowicz. 23 maja 2013

5. Rekurencja. Przykłady

Proste programy w C++ zadania

Zadanie 1. Test (6 pkt) Zaznacz znakiem X w odpowiedniej kolumnie P lub F, która odpowiedź jest prawdziwa, a która fałszywa.

6. Pętle while. Przykłady

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

Zadania język C++ Zad. 1. Napisz program wczytujący z klawiatury wiek dwóch studentów i wypisujący informację o tym, który z nich jest starszy.

WYKŁAD 8. Funkcje i algorytmy rekurencyjne Proste przykłady. Programy: c3_1.c..., c3_6.c. Tomasz Zieliński

Lab 9 Podstawy Programowania

ALGORYTMY MATEMATYCZNE Ćwiczenie 1 Na podstawie schematu blokowego pewnego algorytmu (rys 1), napisz listę kroków tego algorytmu:

Język C, tablice i funkcje (laboratorium, EE1-DI)

4. Funkcje. Przykłady

Programowanie strukturalne i obiektowe. Funkcje

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Algorytmy i struktury danych. Wykład 4

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

EGZAMIN MATURALNY Z INFORMATYKI

Funkcje. Deklaracja funkcji. Definicja funkcji. Wykorzystanie funkcji w programie.

Instrukcje pętli przykłady. Odgadywanie hasła. 1) Program pyta o hasło i podaje adres, gdy hasło poprawne lub komunikat o błędnym haśle.

wykład II uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - funkcje, tablice i wskaźniki wykład II dr Jarosław Mederski Spis

Zajęcia 4 procedury i funkcje

TEORETYCZNE PODSTAWY INFORMATYKI

Instrukcje sterujące

1 Powtórzenie wiadomości

3. Instrukcje warunkowe

Programowanie komputerowe. Zajęcia 2

Poprawność semantyczna

REKURENCJA W JĘZYKU HASKELL. Autor: Walczak Michał

Algorytmika i programowanie. Wykład 2 inż. Barbara Fryc Wyższa Szkoła Informatyki i Zarządzania w Rzeszowie

Rekurencja. Przygotowała: Agnieszka Reiter

Wykład 8. Rekurencja. Iterować jest rzeczą ludzką, wykonywać rekursywnie boską. L. Peter Deutsch

Wiadomości wstępne Środowisko programistyczne Najważniejsze różnice C/C++ vs Java

1. Liczby wymierne. x dla x 0 (wartością bezwzględną liczby nieujemnej jest ta sama liczba)

Algorytm. a programowanie -

Część 4 życie programu

for (inicjacja_warunkow_poczatkowych(końcowych); wyrazenie_warunkowe; wyrazenie_zwiekszajace(zmniejszające)) { blok instrukcji; }

Programowanie w Baltie klasa VII

Teoretyczne podstawy informatyki

1. Napisz program, który wyświetli Twoje dane jako napis Witaj, Imię Nazwisko. 2. Napisz program, który wyświetli wizytówkę postaci:

Laboratorium nr 1. i 2.

WHILE (wyrażenie) instrukcja;

1 Podstawy c++ w pigułce.

7. Pętle for. Przykłady

WHILE (wyrażenie) instrukcja;

Zadanie 1. Zmiana systemów. Zadanie 2. Szyfr Cezara. Zadanie 3. Czy liczba jest doskonała. Zadanie 4. Rozkład liczby na czynniki pierwsze Zadanie 5.

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk

Strategia "dziel i zwyciężaj"

Tablice mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Informatyka wprowadzenie do algorytmów (II) dr hab. inż. Mikołaj Morzy

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Klasa 2 INFORMATYKA. dla szkół ponadgimnazjalnych zakres rozszerzony. Założone osiągnięcia ucznia wymagania edukacyjne na. poszczególne oceny

*W uproszczeniu: jest dziewięciu sędziów przyznających po dwie noty: za wartość techniczną i artystyczną (skala od 0.0 do 6.0)

Podstawy Programowania Podstawowa składnia języka C++

2. Tablice. Tablice jednowymiarowe - wektory. Algorytmy i Struktury Danych

Język C, tablice i funkcje (laboratorium)

Podstawy programowania Laboratorium. Ćwiczenie 2 Programowanie strukturalne podstawowe rodzaje instrukcji

do instrukcja while (wyrażenie);

Analiza algorytmów zadania podstawowe

Funkcje wymierne. Jerzy Rutkowski. Działania dodawania i mnożenia funkcji wymiernych określa się wzorami: g h + k l g h k.

Algorytmy i język C++

Konstrukcje warunkowe Pętle

C++ wprowadzanie zmiennych

1. Napisz program wypisujący w kolejnych wierszach standardowego wyjścia pojedyncze słowa następującego napisu Bardzo dlugi napis. 2.

Uwaga: Funkcja zamień(a[j],a[j+s]) zamienia miejscami wartości A[j] oraz A[j+s].

Podstawy Programowania C++

Programowanie dynamiczne

1 Podstawy c++ w pigułce.

for (inicjacja_warunkow_poczatkowych; wyrazenie_warunkowe; wyrazenie_zwiekszajace) { blok instrukcji; }

Metody Metody, parametry, zwracanie wartości

Część XVII C++ Funkcje. Funkcja bezargumentowa Najprostszym przypadkiem funkcji jest jej wersja bezargumentowa. Spójrzmy na przykład.

Zadania do samodzielnego rozwiązania

FUNKCJA REKURENCYJNA. function s(n:integer):integer; begin if (n>1) then s:=n*s(n-1); else s:=1; end;

Funkcje. Wprowadzenie. Mirosław Ochodek

Zaawansowane algorytmy i struktury danych

Projektowanie algorytmów rekurencyjnych

Programowanie komputerowe. Zajęcia 4

Pzetestuj działanie pętli while i do...while na poniższym przykładzie:

Przykładowe zadania z teorii liczb

1. LICZBY DZIAŁ Z PODRĘCZNIKA L.P. NaCoBeZu kryteria sukcesu w języku ucznia

> C++ dynamiczna alokacja/rezerwacja/przydział pamięci. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Funkcje. Spotkanie 5. Tworzenie i używanie funkcji. Przekazywanie argumentów do funkcji. Domyślne wartości argumentów

Programowanie - instrukcje sterujące

do instrukcja while(wyrażenie);

2. Zmienne i stałe. Przykłady Napisz program, który wypisze na ekran wynik dzielenia 281 i 117 w postaci liczby mieszanej (tj. 2 47/117).

IX. Wskaźniki.(3 godz.)

ALGORYTMY Algorytm poprawny jednoznaczny szczegółowy uniwersalny skończoność efektywność (sprawność) zmiennych liniowy warunkowy iteracyjny

Schematy blokowe I. 1. Dostępne bloki: 2. Prosty program drukujący tekst.

Transkrypt:

Rekurencja (rekursja) Rekurencja wywołanie funkcji przez nią samą wewnątrz ciała funkcji. Rekurencja może być pośrednia funkcja jest wywoływana przez inną funkcję, wywołaną (pośrednio lub bezpośrednio) przez samą funkcję, np.: Listing 1 void a(int i) return b(i); void b(int i) i--; return a(i) lub bezpośrednia funkcja wywołuje samą siebie bezpośrednio wewnątrz ciała funkcji: Listing 2 void a(int i) return a(--i); Każda funkcja rekurencyjna musi posiadać warunek zatrzymania (warunek stopu) stan danych, dla którego nie dojdzie do ponownego wywołania funkcji. W przeciwnym razie będzie się wywoływała do momentu przepełnienia stosu (stosu wywołań / stosu podręcznego) [stos jest obszarem pamięci służącym m.in. do przechowywania zmiennych lokalnych funkcji, parametrów wywołania i wartości zwracanej przez funkcję]. W powyższym kodzie warunek stopu to przyjęcie przez zmienną i wartości 0. Zastosowania rekurencji Rekurencję możemy stosować w celu rozwiązania problemów, które wymagają przetworzenia danych, a następnie takiego samego przetworzenia wyników. Zwykle problemy rekurencyjne możemy rozwiązać przy użyciu iteracji (takie rozwiązania są zwykle szybsze od rekurencyjnych odpowiedników). Zaletą rozwiązań rekurencyjnych jest prostota przejrzystość kodu niektóre problemy posiadają proste rozwiązania rekurencyjne i bardzo złożone rozwiązania iteracyjne. Algorytm rekurencyjny Algorytm rekurencyjny- taki, który rozwiązuje problem, przez rozwiązanie pewnej liczby prostszych przypadków tego samego problemu. Algorytmy rekurencyjne implementujemy w C++ przy użyciu funkcji rekurencyjnych. Funkcje rekurencyjne odpowiadają definicjom rekurencyjnym funkcji matematycznych. Zależność rekurencyjna definiuje funkcję, której dziedziną są wartości całkowite nieujemne, przez podanie pewnych jej wartości początkowych i wzoru lub wzorów, w których występują wartości funkcji dla mniejszych argumentów. Przykład problemu rekurencyjnego Napiszmy funkcję, która oblicza N-tą potęgę liczby X, gdzie N jest nieujemną liczbą całkowitą. Zauważmy że X N = X * X N-1 Taka postać to definicja rekursyjna (postać rekurencyjna) problemu przedstawienie go jako zależności między rozwiązaniem problemu dla danego kroku przetwarzania danych i wynikiem z kroku

poprzedniego (ta zależność niekoniecznie dana jest jako prosta zależność arytmetyczna, jak w powyższym przykładzie). Aby móc obliczyć X N-1, musimy znaleźć X N-2, X N-3... czyli rozwiązanie problemu mniejszego o jeden krok przetwarzania. W rekursji problem jest redukowany do prostszej (dla danego problemu) postaci - aż do momentu, gdy możemy znaleźć rozwiązanie bez odwoływania się do postaci rekurencyjnej (w naszym przypadku [X N = X * X N-1 ] ). Taka postać problemu to wspomniany powyżej warunek zatrzymania w naszym przypadku dane, dla których nie jest wymagane odwołanie się do definicji rekurencyjnej problemu to N = 0 (zastanów się, czy warunkiem zatrzymania mogłoby być również N=1, czy dla X=0 opłaci się dodawać odrębny warunek?). Rozważany problem można zapisać przez następującą zależność rekurencyjną: X N Jak będą wyglądały analogiczne zapisy dla problemów silnia i suma? Listing 3 X * X 1 N 1 dla dla N 0 N 0 long double potega(long double X,int N) if (N==0) return 1; return X*potega(X,N-1); Analizując powyższą funkcję możemy zauważyć, że wywołując funkcję potega powodujemy, że będzie ona wywoływała samą siebie, do momentu, gdy parametr N wywołania nie będzie równy 0. Funkcja nie może zwrócić wartości, do momentu, gdy wszystkie wywołania wewnętrzne nie zwrócą wartości. Uproszczony schemat zawartości stosu przedstawia tabela: wywołanie potega(2,0) potega(2,1) potega(2,2) potega(2,3) stos Omówmy krótko sposób działania stosu: funkcja w momencie wywołania rezerwuje na stosie ilość pamięci odpowiadającą typowi zwracanej wartości. Ten obszar zawiera śmieci do momentu zwrócenia wartości przez funkcję. Następnie funkcja rezerwuje obszar pamięci na parametry wywołania, zmienne lokalne i wywołuje się ponownie o ile parametry wywołania na to pozwolą. W momencie, gdy parametry wywołania odpowiadają warunkowi zatrzymania funkcja zwraca wartość, obszar pamięci zarezerwowany na zmienne lokalne i parametry wywołania zostaje zwolniony. Zwracana wartość jest przypisywana jako wartość funkcji w miejscu wywołania funkcji (w naszym przypadku return X*potega(X,N-1);), obszar pamięci zarezerwowany na zwróconą właśnie wartość zostaje zwolniony. Teraz przedostatnie wywołanie funkcji może obliczyć wartość formuły i zwrócić wartość- itd., aż do momentu, gdy można obliczyć wartość pierwszego wywołania funkcji (dla przykładu z tabeli powyżej to wywołanie funkcji potega(2, 3). Wnioski: Ponieważ obszar pamięci zarezerwowany na stos jest ograniczony deklarowanie zbyt wielu zmiennych lokalnych prowadzi dla złożonych problemów do szybkiego jego przepełnienia należy unikać deklarowania zmiennych lokalnych w funkcjach rekurencyjnych, o ile to tylko możliwe. Ponadto rezerwacja miejsca na stosie na zmienne lokalne i parametry wywołania jest kosztowna, jeśli chodzi o czas działania programu w przypadkach, gdy funkcja rekurencyjna wywołuje samą siebie kilkukrotnie (np.

funkcja rekurencyjna obliczająca wartość dwumianu Newtona dla (n,k) lub funkcja rekurencyjna obliczająca wartość n-tego wyrazu ciągu Fibonacciego) czas ten bardzo szybko wzrasta wraz z wartością parametrów wywołania. Przykład programu obliczającego dwumian Newtona przy użyciu kilku wariantów funkcji rekurencyjnej można pobrać tu: http://argon.kul.lublin.pl/~zczajk/cpp/dwumian.html. Inne proste przykłady funkcji rekurencyjnych Rozwiązując problemy rekurencyjne stosujemy 3 kroki: - analiza problemu - identyfikacja warunku zatrzymania - zapisanie zależności rekurencyjnej 1. Napisz funkcję, która wypisze na ekranie n znaków *. Problem posiada banalne rozwiązanie iteracyjne, rozwiązanie rekurencyjne (w tym przypadku nieco na siłę ) uzyskujemy w następujący sposób: - funkcja nie zwraca wartości, natomiast wypisuje znak * - dla n=0 nie wypisuje znaku i kończy działanie - dla n>0 wypisuje znak i rozwiązuje problem wypisania n-1 znaków. Program będzie miał postać: void stars(int n) cout<< * ; return stars(n-1); 2. Napisz funkcję, która wyświetli liczby całkowite od n do 1 Problem analizujemy analogicznie jak poprzedni, podobnie jak poprzedni nie wymaga stosowania rekurencji: - funkcja nie zwraca wartości, natomiast wypisuje liczbę całkowitą - dla n=1 wypisuje 1 i kończy działanie - dla n>1 wypisuje n i rozwiązuje problem wypisania liczb od n-1 do 1 Program będzie miał postać: void reverse(int n) if (n==1) return reverse(n-1); 3. Napisz funkcję, która wyświetli liczby całkowite od 1 do n Problem wymaga niewielkich korekt kodu z poprzedniego punktu: - funkcja nie zwraca wartości, natomiast wypisuje liczbę całkowitą * - dla n=1 wypisuje 1 i kończy działanie - dla n>1 rozwiązuje problem wypisania liczb od 1 do n-1 i wypisuje n

Program będzie miał postać: void integers(int n) if (n==1) integers(n-1); 4. Napisz funkcję rekurencyjną, która zwróci wartość typu string złożoną ze znaków wprowadzonych na klawiaturze. Znaki są wprowadzane do momentu wystąpienia symbolu *. Zwracana wartość typu string składa się ze znaków wprowadzonych przez użytkownika (łącznie z symbolem *), uporządkowanych w kolejności odwrotnej do kolejności wprowadzania. Problem wymaga wykorzystania przeciążonego operatora + klasy string, umożliwiającego scalanie łańcuchów znaków. - niech zmienna s typu string służy do przechowywania wprowadzanych znaków - jeśli s== * funkcja zwraca s - jeśli s!= * funkcja dołącza do znaków wprowadzonych po s znak przechowywany w zmiennej s string odkonca() string s; cout<< Wprowadź znak i wciśnij ENTER ; cin>>s; if(s== * ) //uwaga operator == nie może porównywać typu char z //typem string dlatego nie * lecz "*" return s; return odkonca()+s; Jeśli wprowadzimy każdorazowo kilka znaków, powyższy kod zwróci wprowadzone ciągi znaków w kolejności odwrotnej do kolejności ich wprowadzania. 5. Napisz funkcję rekurencyjną, która wyświetli choinkę o wysokości n: * *** ***** ******* Aby wyświetlić choinkę o rozmiarze N, należy wyświetlić przesuniętą o jeden znak w prawo choinkę o wymiarze N-1, a następnie wyświetlić linię 2N-1 gwiazdek, jeśli N>1, lub niczego nie wyświetlić, jeśli N==0. void choinka(int n, int przesuniecie=0)//jeśli nie zostanie podany 2-gi //parametr wywołania, zostanie użyta wartość domyślna drugiego //parametru, tu ustawiona na 0 choinka(n-1,++przesuniecie); //wyświetlamy choinkę przesuniętą o 1 cout<<setw(przesuniecie)<<setfill(' ')<<"*" <<setw(2*n-1)<<setfill('*')<<'\n';//to jest powtórzenie //materiału o formatowaniu UWAGA! '\n' to też ZNAK!

Spróbuj zastosować funkcje analogiczne do tych z punktu 1 do rozwiązania tego problemu (zastąp nimi manipulatory). Spróbuj napisać program wyświetlający odwróconą choinkę 6. Napisz funkcję rekurencyjną, która obliczy, wykorzystując algorytm Hornera wartość wielomianu postaci a 0 x n + a 1 x n-1 +... + a n-1 x + a n dla podanej jako argument wartości x i danego wektora współczynników t[i]= a i Ponieważ wielomian stopnia n możemy zapisać w następującej postaci, a 0 x n + a 1 x n-1 +... + a n-1 x + a n = (...((a 0 x+ a 1 )*x+ a 2 )*x+... + a n-1 )x + a n, korzystając ze schematu Hornera, możemy zauważyć, że - funkcja zwraca t[0] gdy i==0 - funkcja zwraca sumę: t[i]+x*(wynik funkcji dla i-1) gdy i!=0 W rezultacie rozwiązanie problemu ma postać: long double horner(long double t[], long double x,int i) return t[0]; return x*horner(t,x,i-1)+t[i]; 7. Napisz rekurencyjną funkcję, znajdującą największy wspólny dzielnik 2 liczb całkowitych dodatnich. Algorytm Euklidesa opiera się na spostrzeżeniu, że NWD 2 liczb całkowitych m i n, m>n, jest równy NWD liczb y i x mod y (reszta z dzielenia x przez y). Liczba x dzieli zarówno liczbę m jak i n x dzieli n i resztę z dzielenia m przez n, ponieważ m jest równe sumie m mod n i wielokrotności n. int NWD(int m, int n) return m; return NWD(n,m%n);