12. Rekurencja. UWAGA Trzeba bardzo dokładnie ustalić <warunek>, żeby mieć pewność, że ciąg wywołań się zakończy.

Podobne dokumenty
Rekurencja. Przygotowała: Agnieszka Reiter

Wstęp do programowania

Opis zagadnieo 1-3. Iteracja, rekurencja i ich realizacja

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

Podstawy algorytmiki i programowania - wykład 3 Funkcje rekurencyjne Wyszukiwanie liniowe i binarne w tablicy

funkcje rekurencyjne Wykład 12. Podstawy programowania (język C) Funkcje rekurencyjne (1) Funkcje rekurencyjne (2)

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

Wstęp do Informatyki

Informacje wstępne #include <nazwa> - derektywa procesora umożliwiająca włączenie do programu pliku o podanej nazwie. Typy danych: char, signed char

Programowanie - wykład 4

5. Rekurencja. Przykłady

Podstawy algorytmiki i programowania - wykład 2 Tablice dwuwymiarowe cd Funkcje rekurencyjne

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

Rekurencja (rekursja)

Podstawowe elementy proceduralne w C++ Program i wyjście. Zmienne i arytmetyka. Wskaźniki i tablice. Testy i pętle. Funkcje.

ALGORYTMY I STRUKTURY DANYCH

Wstęp do informatyki- wykład 9 Funkcje

Wstęp do programowania

Podstawy programowania. Wykład: 4. Instrukcje sterujące, operatory. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Rozwiązanie. #include <cstdlib> #include <iostream> using namespace std;

Wstęp do informatyki- wykład 11 Funkcje

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Instrukcja wyboru, pętle. 2 wykład. Podstawy programowania - Paskal

Podstawy algorytmiki i programowania - wykład 6 Sortowanie- algorytmy

Programowanie Obiektowe i C++

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Kontrola przebiegu programu

Wstęp do programowania

Programowanie komputerowe. Zajęcia 1

Zadanie 04 Ktory z ponizszych typow danych w jezyku ANSI C jest typem zmiennoprzecinkowym pojedynczej precyzji?

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

#include <stdio.h> int main( ) { int x = 10; long y = 20; double s; s = x + y; printf ( %s obliczen %d + %ld = %f, Wynik, x, y, s ); }

Podstawy programowania skrót z wykładów:

Wstęp do informatyki- wykład 12 Funkcje (przekazywanie parametrów przez wartość i zmienną)

Wykład 3. Metoda dziel i zwyciężaj

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

Część 4 życie programu

Projektowanie algorytmów rekurencyjnych

1 Podstawy c++ w pigułce.

Język ludzki kod maszynowy

Wstęp do programowania

Rekurencja/rekursja. Iluzja istnienia wielu kopii tego samego algorytmu (aktywacji) Tylko jedna aktywacja jest aktywna w danej chwili

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 5 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 41

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

Raport z projektu. Przedmiot: Algorytmy i struktury danych 1 Projekt: Wieża Hanoi Autor: Wojciech Topolski

Podstawy programowania w języku C i C++

typ_zwracanej_wartości nazwa_funkcji(lista deklaracji argumentów) { ciało(treść) funkcji return Val; //zwracana wartość }

Programowanie komputerowe. Zajęcia 2

Wstęp do informatyki- wykład 6

Wstęp do informatyki- wykład 7

Poprawność semantyczna

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

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 5

Programowanie komputerowe. Zajęcia 3

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Zadanie 2: Arytmetyka symboli

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Laboratorium 3. Instrukcje wyboru

Wstęp do informatyki- wykład 9 Pętla while, do while,for -pętla w pętli- przykłady Funkcje

1 Podstawy c++ w pigułce.

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

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

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

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

I - Microsoft Visual Studio C++

Projektowanie klas c.d. Projektowanie klas przykład

Liczby całkowite i rzeczywiste

Ćwiczenie 7 z Podstaw programowania. Język C++, programy pisane w nieobiektowym stylu programowania. Zofia Kruczkiewicz

2.4 Dziedziczenie. 2.4 Dziedziczenie Przykłady programowania w C - kurs podstawowy

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

Struktury Struktura polami struct struct struct struct

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Szablony funkcji i szablony klas

Wstęp do informatyki- wykład 5 Instrukcja selekcji if-else Operatory arytmetyczne i logiczne

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

Programowanie obiektowe i C++ dla matematyków

wykład IV uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C, a C++. wykład IV dr Jarosław Mederski Spis Język C++ - wstęp

Techniki Programowania wskaźniki

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

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

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

Wykład II. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Pytania sprawdzające wiedzę z programowania C++

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Programowanie Procedurale

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

Wprowadzenie do szablonów szablony funkcji

typ y y p y z łoż o on o e n - tab a lice c e w iel e owym m ar a o r we, e stru r kt k ury

Programowanie w C++ Wykład 3. Katarzyna Grzelak. 12 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 35

Wstęp do Programowania 2

Języki programowania - podstawy

Algorytmy. wer Wojciech Myszka 30 listopada 2008

Wprowadzenie do szablonów szablony funkcji

Funkcje i tablice. Elwira Wachowicz. 23 maja 2013

WYKŁAD 10. Zmienne o złożonej budowie Statyczne i dynamiczne struktury danych: lista, kolejka, stos, drzewo. Programy: c5_1.c, c5_2, c5_3, c5_4, c5_5

Wstęp do programowania 1

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

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

W języku C dostępne są trzy instrukcje, umożliwiające tworzenie pętli: for, while oraz do. for (w1;w2;w3) instrukcja

Rekurencja. Dla rozwiązania danego problemu, algorytm wywołuje sam siebie przy rozwiązywaniu podobnych podproblemów. Przykład: silnia: n! = n(n-1)!

Transkrypt:

12. Rekurencja. Funkcja rekurencyjna funkcja, która wywołuje samą siebie. Naturalne postępowanie: np. zbierając rozsypane pionki do gry podnosi się zwykle pierwszy, a potem zbiera się resztę w ten sam sposób. Schemat funkcji: void rekur (<listaargumentow>) <instrukcje>; if ( <warunek>) rekur (<listaargumentow2>) <instrukcje> UWAGA Trzeba bardzo dokładnie ustalić <warunek>, żeby mieć pewność, że ciąg wywołań się zakończy. 12.1 Wykorzystanie stosu. Kolejne wywołania z parametrami są umieszczane na stosie. Rekurencja jawna jest możliwa tylko w językach obsługujących stos wywołań! Przykład 1 (rek_p1) #include <iostream> using namespace std; const int NMAKS = 15; long int SILNIA(int n) cout<<"n = "<<n<<endl; if(n<=1) return 1; else return n * SILNIA(n-1); int main () int liczba; cout<<"jakiej liczby policzymy silnie? "; cin>>liczba; if (liczba > NMAKS) cout<<"******niestety za duza liczba"<<endl; else if (liczba<0) cout<<"******liczba ujemna"<<endl; else cout<<"silnia liczby "<<liczba<<" wynosi " << SILNIA(liczba)<<endl; return 0; 1

Realizacja 2

12.2 Linijka. Strategia: dziel i zwyciężaj Rysowanie podziałki na linijce: 1. zaznacz dwa końce; 2. znajdź i zaznacz środek; 3. wykonaj to samo dla lewej połowy podziału 4. wykonaj to samo dla prawej połowy podziału /* ruler.cpp - uzycie rekurencji do dzielenia linijki */ #include <iostream> using namespace std; const int Len = 66; const int Divs = 6; void subdivide(char ar[], int low, int high, int level); int main() char ruler[len]; int i, j; int max, min; for (i = 1; i < Len - 2; i++) ruler[i] = ' '; ruler[len - 1] = '\0'; max = Len - 2; min = 0; ruler[min] = ruler[max] = ' '; cout<<ruler<<endl; for (i = 1; i <= Divs; i++) subdivide(ruler,min,max, i); cout<< ruler<<endl; for (j = 1; j < Len - 2; j++) ruler[j] = ' '; /* zerowanie linijki */ return 0; void subdivide(char ar[], int low, int high, int level) int mid; if (level == 0) /* zmienna level kontroluje poziom rekurencji */ /* przy każdym wywołaniu zmniejszana o 1 */ return; mid = (high + low) / 2; ar[mid] = ' '; subdivide(ar, low, mid, level - 1); subdivide(ar, mid, high, level - 1); 3

12.3 Ciągi zdefiniowane rekurencyjnie. Ciąg Fibonacciego Obliczanie parametrów ciągu tego i innych, podobnych typów, wykonane bezpośrednio na podstawie wzoru matematycznego, może powodować problemy w postaci zbyt wielu dublujących się obliczeń. Ciąg Fibonacciego jest zdefiniowany rekurencyjnie, w sposób analogiczny do definicji funkcji silnia: F(0) = 1 F(1) = 1 F(n) = F(n-1) + F(n-2) Zadanie obliczania elementów takiego ciągu można w języku C zapisać niemal dokładnie tak samo, jak wyraża to wzór definicyjny: unsigned long int FIB( int n ) if (n<2) return 1; else return FIB(n-1) + FIB(n-2); Schemat wywołań rekurencyjnych, jak wykaże prosta analiza kodu, doprowadzi do następującego drzewa wywołań: FIB(4) FIB(3) FIB(2) FIB(2) FIB(1) FIB(1) FIB(0) FIB(1) FIB(0) Drzewo wywołań funkcji FIB dla parametru 4. Gałęzie zaznaczone kolorem wykonują się dwa razy. Zupełnie niepotrzebnie. W sumie, wywołanie funkcji FIB dla większych parametrów n, spowoduje że wykonane zostanie w przybliżeniu 2n obliczeń, co jest z oczywistych powodów nieefektywne. Dlatego należy pamiętać, że nie jest dobrą metodą programowanie rekurencyjne tam, gdzie wystarczą proste funkcje iteracyjne. 4

Przykład 3 /* Fibonacii.cpp - uzycie rekurencji do wyliczania elementów ci¹gu Fibonacci'ego. */ #include <iostream> using namespace std; unsigned long int FIB( int n ); unsigned long int FIB2( int n ); unsigned long int FIB3( int n ); int main() int zakres; int i; cout<<"ile elementow chcesz tworzyc? "; cin>>zakres; cout<<"********* Rekurencyjnie"<<endl; for (i=0; i<=zakres; ++i) cout<<" fib("<<i<<")= "<<FIB(i)<<endl; cout<<"********* Iteracyjnie"<<endl; FIB2(zakres); cout<<"********* Iteracyjnie bez przechowywania"<<endl; FIB3(zakres); return 0; unsigned long int FIB( int n ) if (n<2) return 1; else return FIB(n-1) + FIB(n-2); unsigned long int FIB2( int n ) int i; unsigned long int *fibtab= new unsigned long int[n+1]; if (n<2) return 1; else fibtab[0]=fibtab[1]=1; for (i=2; i<=n; ++i) fibtab[i]=fibtab[i-1]+fibtab[i-2]; cout<<"fib2("<<i<<") = "<< fibtab[i]<<endl; return fibtab[n]; unsigned long int FIB3( int n ) int i; /* Jeżeli nie trzeba przechowywac wynikow posrednich */ long an, an1, an2; an1 = an2 = 1; 5

for (i = 2; i<=n; ++i) an = an1 + an2; cout<<"fib3("<<i<<") = "<< an<<endl; an2 = an1; an1 = an; return an; 12.4 Wieże Hanoi. Wieże Hanoi problem polegający na odbudowaniu, z zachowaniem kształtu, wieży z krążków o różnych średnicach (popularna dziecięca zabawka), przy czym podczas przekładania wolno się posługiwać buforem (reprezentowanym w tym przypadku przez dodatkowy słupek), jednak przy ogólnym założeniu, że nie wolno kłaść krążka o większej średnicy na mniejszy ani przekładać kilku krążków jednocześnie. Jest to przykład zadania, którego złożoność obliczeniowa wzrasta niezwykle szybko w miarę zwiększania parametru wejściowego, tj. liczby elementów wieży. Dla n krążków złożoność wynosi: 2 n -1 Rysunek 1 Od lewej: słupek A z całą wieżą, pusty słupek B pełniący rolę bufora i pusty słupek docelowy C http://upload.wikimedia.org/wikipedia/commons/6/60/tower_of_hanoi_4.gif Wieże Hanoi można łatwo rozwiązać za pomocą prostego algorytmu rekurencyjnego lub iteracyjnego. Oznaczmy kolejne słupki literami A, B i C. Niech n będzie liczbą krążków, które chcemy przenieść ze słupka A na słupek C posługując się słupkiem B jako buforem. 6

Rozwiązanie rekurencyjne Algorytm rekurencyjny składa się z następujących kroków: 1. przenieś (rekurencyjnie) n-1 krążków ze słupka A na słupek B posługując się słupkiem C, 2. przenieś jeden krążek ze słupka A na słupek C, 3. przenieś (rekurencyjnie) n-1 krążków ze słupka B na słupek C posługując się słupkiem A. Przykładowa implementacja /* Problem wiez Hanoi. Trzy slupki oznaczone A,B i C. Program wyswietla kolejnosc ruchów - z ktorego na ktory slupek. */ #include <iostream> using namespace std; int NrRuchu = 1; void hanoi(int n, char A, char B, char C) /* przeklada n krazków z A korzystajac z B na C */ if (n > 0) hanoi(n-1, A, C, B); cout<<nrruchu++<<'.'<< A<<" -> "<< C<<endl; hanoi(n-1, B, A, C); int main() int LiczbaKrazkow; cout<<"podaj liczbe krazkow "; cin>>liczbakrazkow; while (LiczbaKrazkow<=0 LiczbaKrazkow>10) cout<<"bledna liczba klockow. Podaj jeszcze raz liczbe od 1 do 10." <<endl; cout<<"podaj liczbe krazkow "; cin>>liczbakrazkow; hanoi(liczbakrazkow, 'A', 'B', 'C'); return 0; 7

12.5 Analiza składniowa metodą zejść rekurencyjnych. Gramatyka bezkontekstowa wyrażeń arytmetycznych: <wyrażenie> ::= <składnik> + <wyrażenie> <składnik> - <wyrażenie> <składnik> <składnik> ::= <czynnik> * <składnik> <czynnik> /<składnik> <czynnik> <czynnik> ::= <liczba> (<wyrażenie>) Podstawy programowania. Wykład 08 rekurencja Dla każdego symbolu nieterminalnego (umieszczony w nawiasach <>) piszę funkcję. Przykład 5. Kalkulator wyrażeń arytmetycznych z nawiasami. #include <iostream> //w12p5.cpp using namespace std; /*<wyrazenie> ::= <skladnik> + <wyrazenie> <skladnik> - <wyrazenie> <skladnik> <skladnik> ::= <czynnik> * <skladnik> <czynnik> /<skladnik> <czynnik> ::= <liczba> (<wyrazenie>). */ typedef enum PlusSm,MinusSm,RazySm, DzielSm,LewyNawSm, PrawyNawSm, LiczbaSm, PustySm symbole; #define MAXBUF 101 struct symb symbole SymbNm; double wart; symbol; char buforek[maxbuf]; int pozbuf = 0; void scan() /* pobiera z bufora kolejny symbol */ int wart; char znak; while ((znak = buforek[pozbuf++])== ' '); /* zjadamy spacje z poczatku */ if (isdigit(znak)) symbol.symbnm = LiczbaSm; /* argumenty tylko calkowite */ wart = znak - '0'; while (isdigit(znak = buforek[pozbuf])) 8

wart = 10*wart+ (znak - '0'); pozbuf++; symbol.wart = wart; else switch (znak) case '(': symbol.symbnm = LewyNawSm; case ')': symbol.symbnm = PrawyNawSm; case '+': symbol.symbnm = PlusSm; case '-': symbol.symbnm = MinusSm; case '*': symbol.symbnm = RazySm; case '/': symbol.symbnm = DzielSm; case '\0': symbol.symbnm = PustySm; default: cout<<"nieznany znak "<< znak<<endl; double skladnik(); double czynnik(); double wyrazenie() double wart; Podstawy programowania. Wykład 08 rekurencja wart = skladnik(); if (symbol.symbnm == PlusSm) /* zjadam + */ wart += wyrazenie(); else if (symbol.symbnm == MinusSm) wart -= wyrazenie(); cout<<"wwwwwwwwwwwwyrazenie = "<< wart<<endl; return wart; double skladnik() double wart; 9

wart = czynnik(); if (symbol.symbnm == RazySm) wart *= skladnik(); else if (symbol.symbnm == DzielSm) wart /= skladnik(); cout<<"sssssssssssssskladnik = "<<wart<<endl; return wart; double czynnik () double wart; if (symbol.symbnm == LiczbaSm) wart = symbol.wart; else if (symbol.symbnm == LewyNawSm) wart = wyrazenie(); if (symbol.symbnm!= PrawyNawSm) cout<<"**** Spodziewany nawias zamykajacy"<<endl; wart = 0; else cout<<"$$$$$ czynnik wart = "<<wart<<endl; return wart; int main() int i=0; char c; cout<< "Podaj wyrazenie "; while ((c = cin.get())!= '\n')buforek[i++] = c; buforek[i]='\0'; cout<<"wczytano "<<buforek<<' '<<endl; symbol.wart = 0; cout<< "= "<< wyrazenie()<<endl; return 0; 10