Dekompozice problému, rekurze

Podobné dokumenty
Rozklad problému na podproblémy, rekurze

Rozklad problému na podproblémy, rekurze

5. přednáška - Rozklad problému na podproblémy

Rozklad problému na podproblémy

8. Rekurze. doc. Ing. Jiří Vokřínek, Ph.D. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze

4. Rekurze. BI-EP1 Efektivní programování Martin Kačer

Algoritmizace a programování

Rekurze. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Funkce, procedury, složitost

Rekurzivní algoritmy

Příkazy if, while, do-while, for, switch

Funkce, intuitivní chápání složitosti

5. Dynamické programování

IB111 Úvod do programování skrze Python

Funkce pokročilé možnosti. Úvod do programování 2 Tomáš Kühr

BI-PA1 Programování a Algoritmizace 1. Miroslav Baĺık, Ladislav Vagner a Josef Vogel. 7., 9. a 10. listopadu 2017

Časová složitost algoritmů, řazení a vyhledávání

Algoritmy. BI-PA1 Programování a Algoritmizace I. Ladislav Vagner

Základy algoritmizace 4. Problémy, algoritmy, data

Mělká a hluboká kopie

Abstraktní datové typy

Ukazatele #1, struktury

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Abstraktní datové typy, moduly

Programování: základní konstrukce, příklady, aplikace. IB111 Programování a algoritmizace

Jazyk C++, některá rozšíření oproti C

Začínáme vážně programovat. Řídící struktury Přetypování Vstupně výstupní operace Vlastní tvorba programů

V případě jazyka Java bychom abstraktní datový typ Time reprezentující čas mohli definovat pomocí třídy takto:

Seminář z IVT Algoritmizace. Slovanské gymnázium Olomouc Tomáš Kühr

VYŠŠÍ ODBORNÁ ŠKOLA a STŘEDNÍ PRŮMYSLOVÁ ŠKOLA Mariánská 1100, Varnsdorf PROGRAMOVÁNÍ FUNKCE, REKURZE, CYKLY

Rekurze a rychlé třídění

Abstraktní třídy, polymorfní struktury

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Operační systémy. Cvičení 4: Programování v C pod Unixem

Dynamické datové struktury III.

Dynamické programování

IB111 Úvod do programování skrze Python

Rekurze. Pavel Töpfer, 2017 Programování 1-8 1

2 Strukturované datové typy Pole Záznam Množina... 4

Algoritmizace řazení Bubble Sort

IB108 Sada 1, Příklad 1 Vypracovali: Tomáš Krajča (255676), Martin Milata (256615)

5 Rekurze a zásobník. Rekurzivní volání metody

Úvod do programování 10. hodina

Funkce a procedury. Jan Faigl. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze. Přednáška 5 A0B36PR1 Programování 1

Prohledávání do šířky = algoritmus vlny

Základní datové struktury

Programovani v Maplu Procedura

Část 1 Funkce, lokální proměnné a přidělování paměti. Funkce a procedury. Část 2 Cykly a řízení jejich průchodu. Část 3 Příklady

V každém kroku se a + b zmenší o min(a, b), tedy vždy alespoň o 1. Jestliže jsme na začátku dostali 2

Algoritmizace a programování

ÚVODNÍ ZNALOSTI. datové struktury. správnost programů. analýza algoritmů

Náplň. v Jednoduché příklady na práci s poli v C - Vlastnosti třídění - Způsoby (algoritmy) třídění

Ukazatele #2, dynamická alokace paměti

C++ Akademie SH. 2. Prom nné, podmínky, cykly, funkce, rekurze, operátory. Michal Kvasni ka. 20. b ezna Za áte níci C++

9. lekce Úvod do jazyka C 4. část Funkce, rekurze Editace, kompilace, spuštění Miroslav Jílek

Základní datové struktury III: Stromy, haldy

Úvod do programovacích jazyků (Java)

Programování v jazyce C pro chemiky (C2160) 3. Příkaz switch, příkaz cyklu for, operátory ++ a --, pole

Algoritmy. BI-PA1 Programování a Algoritmizace 1. Miroslav Baĺık, Ladislav Vagner a Josef Vogel

Mimo samotné správnosti výsledku vypočteného zapsaným algoritmem je ještě jedno

Rekurze. Jan Hnilica Počítačové modelování 12

8. lekce Úvod do jazyka C 3. část Základní příkazy jazyka C Miroslav Jílek

Martin Milata, Pokud je alespoň jeden rozměr čokolády sudý (s výjimkou tabulky velikosti 1x2, která už je od

Pole a kolekce. v C#, Javě a C++

ALG 09. Radix sort (přihrádkové řazení) Counting sort. Přehled asymptotických rychlostí jednotlivých řazení. Ilustrační experiment řazení

Stromy, haldy, prioritní fronty

Časová a prostorová složitost algoritmů

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Binární soubory (datové, typované)

MATURITNÍ OTÁZKY ELEKTROTECHNIKA - POČÍTAČOVÉ SYSTÉMY 2003/2004 PROGRAMOVÉ VYBAVENÍ POČÍTAČŮ

BI-EP1 Efektivní programování 1

Algoritmizace Dynamické programování. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

Rekurze. IB111 Úvod do programování skrze Python

DobSort. Úvod do programování. DobSort Implementace 1/3. DobSort Implementace 2/3. DobSort - Příklad. DobSort Implementace 3/3

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

for (int i = 0; i < sizeof(hodnoty) / sizeof(int); i++) { cout<<hodonoty[i]<< endl; } cin.get(); return 0; }

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Lineární datové struktury

KTE / ZPE Informační technologie

IB015 Neimperativní programování. Časová složitost, Typové třídy, Moduly. Jiří Barnat Libor Škarvada

Řídící struktury, if, while, switch

Test prvočíselnosti. Úkol: otestovat dané číslo N, zda je prvočíslem

Martin Flusser. Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague. October 17, 2016

Úvod do programování. Lekce 5

Algoritmizace a programování

Úvod do programování - Java. Cvičení č.4

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

Středoškolská technika 2017 PROGRAM NA GENEROVÁNÍ PRVOČÍSEL

Funkce, podmíněný příkaz if-else, příkaz cyklu for

Základy algoritmizace. Hašování

Algoritmizace a programování

for (i = 0, j = 5; i < 10; i++) { // tělo cyklu }

Základy programování (IZP)

ALG 14. Vícedimenzionální data. Řazení vícedimenzionálních dat. Experimentální porovnání řadících algoritmů na vícedimenzionálních datech

Prioritní fronta, halda

Struktura programu v době běhu

Algoritmizace a programování. Ak. rok 2012/2013 vbp 1. ze 44

Přednáška 3. Rekurze 1

6 Příkazy řízení toku

Konstruktory a destruktory

Transkript:

Dekompozice problému, rekurze BI-PA1 Programování a Algoritmizace 1 Ladislav Vagner, Josef Vogel Katedra teoretické informatiky a Katedra softwarového inženýrství Fakulta informačních technologíı České vysoké učení technické v Praze xvagner@fit.cvut.cz, vogeljos@fit.cvut.cz 5. prosince 2016 a 8. prosince 2016

Přehled Dekompozice problému. Rekurze. Rekurze a efektivita. Rekurze příklady. Algoritmus merge sort. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 2/43

Dekompozice problému Návrh programu dekompozice problému: pokusíme se rozložit komplexní problém (výsledný program) na jednodušší problémy (podproblémy), užitím pseudokódu (abstraktní příkazy) vyřešíme podproblémy, užitím abstraktních příkazů vytvoříme obrys celého řešení, pro implementaci abstraktních příkazů použijeme funkce. Rozklad problému bude ukázán na variantě hry NIM: je dán celkový počet zápalek (kamenů,... ), např. 15..35, hráč se střídá se strojem v odebírání, je možno odebrat 1, 2 nebo 3 zápalky, prohraje ten, kdo odebere poslední zápalku. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 3/43

Dekompozice problému Návrh programu dekompozice problému: pokusíme se rozložit komplexní problém (výsledný program) na jednodušší problémy (podproblémy), užitím pseudokódu (abstraktní příkazy) vyřešíme podproblémy, užitím abstraktních příkazů vytvoříme obrys celého řešení, pro implementaci abstraktních příkazů použijeme funkce. Rozklad problému bude ukázán na variantě hry NIM: je dán celkový počet zápalek (kamenů,... ), např. 15..35, hráč se střídá se strojem v odebírání, je možno odebrat 1, 2 nebo 3 zápalky, prohraje ten, kdo odebere poslední zápalku. Podproblémy: Inicializace zadání počtu zápalek, odebrání zápalek hráčem, odebrání zápalek strojem. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 3/43

Hra NIM návrh int početzápalek; bool strojnatahu = false; /* zatím abstraktně */ zadání_počtu_zápalek do { if ( strojnatahu ) hraje_počítač else hraje_člověk strojnatahu =! strojnatahu; while ( početzápalek > 0 ); if ( strojnatahu ) stroj_vyhrál; else člověk_vyhrál L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 4/43

Hra NIM implementace (kostra programu) int totalmatches; bool computerturn = false; /* bool budeme definovat */ /*... */ int main ( void ) { gamesetup (); do { if ( computerturn ) computerplayer (); else humanplayer (); computerturn =! computerturn; while ( totalmatches > 0 ); if ( computerturn ) printf("vyhral jsem!\n"); else printf("tys vyhral, gratuluji\n" ); return 0; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 5/43

Hra NIM implementace (zadání počtu zápalek) void gamesetup ( void ) { printf ( "Napis pocet zapalek:\n" ); do { scanf ( "%d", &totalmatches ); /* špatně zadaný počet zápalek neřešíme */ while ( totalmatches < 15 totalmatches > 35 ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 6/43

Hra NIM implementace (hraje člověk) void humanplayer ( void ) { int taken; bool error; printf ( "%d zapalek. Kolik odeberes?\n", totalmatches ); do { scanf ( "%d", &taken ); error = false; if ( taken < 1 ) { error = true; printf ( "nejmene jedna musi byt odebrana!\n" ); if ( taken > 3 taken > totalmatches ) { error = true; printf ( "prilis mnoho odebrano!\n" ); while ( error ); totalmatches -= taken; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 7/43

Hra NIM implementace (hraje stroj) Počet zápalek nevýhodný pro protihráče je 1, 5, 9,..., obecně 4n + 1 zápalek: Když je počet zápalek 4n + 1 a hráč odebere a = 1, 2, 3 zápalky, tak inteligentní protihráč odebere b = 3, 2, 1 zápalek, po dvou kolech je počet zápalek zmenšen o 4 na 4(n 1) + 1, tudíž počet zápalek je zmenšen na 1 po 2n kolech a protihráč prohrává. Když zbývá p zápalek, stroj odebírá x zápalek aby platilo: p x = 4n + 1 x = (p 1) mod 4 Když z formule vyjde x = 0, tak je to stav pro stroj nevýhodný. Pak stroj prohraje, pokud člověk zná správnou strategii, Tak když x = 0, stroj odebere x = 1 zápalku doufaje, že lidský protihráč udělá chybu. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 8/43

Hra NIM implementace (hraje stroj) void computerplayer ( void ) { int taken; taken = ( totalmatches - 1 ) % 4; if ( taken == 0 ) taken = 1; printf ( "%d zapalek. Stroj odebira %d.\n", totalmatches, taken ); totalmatches -= taken; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 9/43

Hra NIM implementace (typ bool) V jazyce C neexistuje datový typ bool. Tento typ můžeme definovat takto: #define bool char #define true 1 #define false 0 po této definici všechny výskyty bool, true, a false jsou nahrazeny char, 1, a 0. Proto: bool computerturn = false; /* tato deklarace je zpracována preprocesorem kompilátoru na: */ char computerturn = 0; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 10/43

Hra NIM implementační poznámka Implementace používá globální proměnnou totalmatches a computerturn. Použití globálních proměnných není dobrá technika: je příčinou problémů ve vícevláknových programech, je příčinou problémů s rekurzivními/reentrantními funkcemi, údržba a integrace software je složitější. Proměnná computerturn se používá jedině ve funkci main a dát ji sem jako lokální je triviální úprava. Proměnná totalmatches je použita pro předání informace mezi funkcemi. Místo globální proměnné lze užít vstupní parametr a návratovou hodnotu (nebo alternativně použít jako parametr referenci in/out parametr). Funkce gamesetup vrátí počet zápalek jako návratovou hodnotu, funkce computerplayer a humanplayer použijí počet zápalek jako in/out parametr. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 11/43

Rekurze Rekurze: viz Rekurze. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 12/43

Rekurze Rekurze: viz Rekurze. tato vtipná definice bývá dávána do knih o programování, ukazuje však spíše na nepochopení rekurze ze strany autora knihy. Rekurze: pokud neznáte význam tohoto pojmu, pokračujte pojmem Rekurze. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 12/43

Rekurze Rekurze: viz Rekurze. tato vtipná definice bývá dávána do knih o programování, ukazuje však spíše na nepochopení rekurze ze strany autora knihy. Rekurze: pokud neznáte význam tohoto pojmu, pokračujte pojmem Rekurze. Nepřímá rekurze: pokud neznáte význam tohoto pojmu, pokračujte pojmem Rekurzivní volání. Rekurzivní volání: pokud neznáte význam tohoto pojmu, pokračujte pojmem Nepřímá rekurze. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 12/43

Rekurze Rekurze je takový způsob programování, kde funkce k vyřešení problému volá sebe sama. Pro použití rekurze musí být splněny dvě obecné podmínky: rekurzivní volání řeší menší (jednodušší) instanci původního problému, existuje nějaká triviální (jednoduchá) instance problému, kde rekurze končí. Příklad rekurzivního algoritmu výpočet faktoriálu: 0! = 1 n! = 1 pro záporné n, n! = n (n 1)! pro kladné n. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 13/43

Rekurze příklady Faktoriál iterativně: int factorialiter ( int n ) { int f = 1; while ( n > 1) { f *= n; n--; return f; Faktoriál rekurzivně: int factorialrec ( int n ) { if ( n <= 1 ) return 1; return n * factorialrec ( n - 1 ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 14/43

Rekurze příklady Iterativní algoritmus nalezení největšího společného dělitele: int gcd (int x, int y) { int remainder; while( y!= 0 ) { remainder = x % y; x = y; y = remainder; return x; Kolik iterací se provede? L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 15/43

Rekurze příklady Iterativní algoritmus nalezení největšího společného dělitele: int gcd (int x, int y) { int remainder; while( y!= 0 ) { remainder = x % y; x = y; y = remainder; return x; Kolik iterací se provede? Necht x y > 0. Pak x mod y < x 2. Důkaz: y x 2. Pak nerovnost platí z definice. y > x 2. Pak x mod y = x y < x 2. Proto je hodnota max(x, y) půlena každé dvě iterace, což vede na 2 log 2 (max(x, y)) iterací. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 15/43

Rekurze příklady Rekurzivní algoritmus nalezení největšího společného dělitele: int gcd ( int x, int y ) { if ( x == y ) return x; if ( x > y ) return gcd ( x % y, y ); else return gcd ( x, y % x ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 16/43

Rekurze a dekompozice problému Problém: přečíst sekvenci celých čísel zakončenou 0, vypsat sekvenci v obráceném pořadí. Dekompozice: obrácení sekvence může být dekomponováno na zpracování prvního prvku a obrácení zbytku sekvence: obrat ( x <rest> ) = obrat ( <rest> ) x. my použijeme abstraktní příkaz obrat, ten obrátí zbytek sekvence ze vstupu, příkaz bude obsahovat tři kroky: 1 čtení čísla x sekvence ze vstupu, 2 obrácení zbytku sekvence reverse, 3 výpis x. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 17/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: stack: x=1 Input: Output: 1 7 4 9 0 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: stack: x=1 x=7 Input: Output: 7 4 9 0 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: stack: x=1 x=7 x=4 Input: Output: 4 9 0 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: stack: x=1 x=7 x=4 x=9 Input: 9 0 Output: L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: write x stack: x=1 x=7 x=4 x=9 x=0 Input: Output: 0 0 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: write x write x stack: x=1 x=7 x=4 x=9 x=0 Input: Output: 0 9 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: write x write x write x stack: x=1 x=7 x=4 x=9 x=0 Input: Output: 0 9 4 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: write x write x write x stack: x=1 x=7 x=4 x=9 x=0 write x Input: Output: 0 9 4 7 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému Postup při výpisu sekvence v obráceném pořadí: write x write x write x stack: x=1 x=7 x=4 x=9 x=0 write x write x Input: Output: 0 9 4 7 1 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 18/43

Rekurze a dekompozice problému #include <stdio.h> void reverse( void ) { int x; scanf ( "%d", &x ); if ( x!= 0 ) reverse (); printf ( "%d ", x ); int main( void ) { printf ( "Napis cisla zakoncena 0:\n" ); reverse (); printf ( "\n" ); return 0; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 19/43

Rekurze a dekompozice problému Hanojské věže Zlaté disky jsou na koĺıku #1, Disky mohou být přesouvány jen po jednom. Disk může být přesunut na kterýkoli prázdný koĺık nebo na koĺık s větším diskem (disky). Disky nemohou být umístěny mimo koĺıky. Úlohou je přemístit věž z koĺıku #1 na koĺık #2. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 20/43

Rekurze a dekompozice problému Hanojské věže Dekompozice definujeme abstraktní příkaz přesuň věž ( n, x, y, z ). Význam je tento: Přesun věže výšky n z koĺıku x na koĺık y s pomocí přechodného uložení na koĺıku z. Příkaz rozložíme na tři kroky: přesuň věž ( n-1, x, z, y ), přesun disku z koĺıku x na koĺık y přesuň věž ( n-1, z, y, x ). L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 21/43

Rekurze a dekompozice problému Hanojské věže #include <stdio.h> void movetower ( int h, int from, int to, int tmp ) { if ( h > 0 ) { movetower ( h-1, from, tmp, to ); printf ( "presun disku z %d na %d\n", from, to ); movetower ( h-1, tmp, to, from ); int main( void ) { int disks; printf ( "Napis pocet disku:\n" ); scanf ( "%d", &disks ); if ( disks > 0 ) movetower ( disks, 1, 2, 3 ); else /*... ošetření chyby... */ return 0; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 22/43

Rekurze a efektivita Rekurzivní funkce jsou přímou implementací rekurzivních algoritmů: rekurzivní algoritmy pracují způsobem shora dolů, triviální instance problému je implementována přímo, rekurzivní volání se použije pro řešení obecné instance problému. Rekurzivní funkce jsou obvykle krátké, přímočaré a snadno srozumitelné. Rekurzivní funkce mají sklon k jisté neefektivnosti. Například stejné jednodušší problémy mohou být řešeny mnohokrát. Mnohé rekurzivní algoritmy mohou být nahrazeny iterativními (např. faktoriál, gcd,... ). Iterativní algoritmy obvykle pracují způsobem zdola nahoru, od instance jednoduchého problému ke komplexnější instanci problému. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 23/43

Fibonacciho posloupnost Pingala (Chhandah-shastra, the Art of Prosody, 450 or 200 př.n.l.) Leonardo Pisano (Leonardo z Pisy), známý jako Fibonacci (cca 1175 1250) kráĺıci. Henry E. Dudeney (1857-1930) krávy. Když má kráva první tele ve věku 2 let a pak každý rok, kolik krav to bude ve 12 letech (žádní býci, žádná kráva nezemře)? počet krav = počet krav vloni + nově narozené (tj. počet krav předloni) f n = f n 1 + f n 2, f 1 = 1, f 2 = 1. n f n n f n n f n 1 1 5 5 9 34 2 1 6 8 10 55 3 2 7 13 11 89 4 3 8 21 12 144 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 24/43

Fibonacciho posloupnost int fibonacci ( int n ) { if ( n <= 2 ) return 1; return fibonacci ( n - 1 ) + fibonacci ( n - 2 ); Rekurzivní řešení je krátké a jednoduché. Kód přímo koresponduje s rekurentní definicí. Je také efektivní? L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 25/43

Fibonacciho posloupnost int fibonacci ( int n ) { int i, fn = 1, fn1 = 1, fn2 = 1; for ( i = 2; i <= n; i ++ ) { fn2 = fn1; fn1 = fn; fn = fn2 + fn1; return fn; Iterativní řešení je poněkud delší. Kód počítá Fibonacciho čísla zdola nahoru (od f 2, f 3,... ). Je efektivní? L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 26/43

Fibonacciho posloupnost int fibonacci ( int n ) { int i, fn = 1, fn1 = 1, fn2 = 1; for ( i = 2; i <= n; i ++ ) { fn2 = fn1; fn1 = fn; fn = fn2 + fn1; return fn; Iterativní řešení je poněkud delší. Kód počítá Fibonacciho čísla zdola nahoru (od f 2, f 3,... ). Je efektivní? Kód provádí n iterací, tj. 3 n sčítání/přiřazení. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 26/43

Fibonacciho posloupnost Iterativní řešení: 3 n operací. Rekurzivní řešení: L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 27/43

Fibonacciho posloupnost Iterativní řešení: 3 n operací. Rekurzivní řešení: f (10) f (9) f (8) f (8) f (7) f (7) f (6) f (7) f (6) f (6) f (5) f (6) f (5) f (5) f (4)........................ Rekurzivní řešení: exponenciální složitost L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 27/43

Fibonacciho posloupnost Iterativní řešení: 3 n operací. Rekurzivní řešení: přibližně 2 n operací. Přímý výpočet (J. Kepler): Zlatý řez ϕ: ϕ = 1 + 5 2 Fibonacciho číslo: 1.6180339887498948482045868343656 f n = ϕn (1 ϕ) n 5 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 28/43

Rekurze násobení /* Iterativní celočíselné násobení, x >= 0 */ int muliter ( int x, int y ) { int res = 0, i; for ( i = 0; i < x; i ++ ) res += y; return res; /* Rekurzivní celočíselné násobení, x >= 0 */ int mulrec ( int x, int y ) { if ( x == 0 ) return 0; return y + mulrec ( x - 1, y ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 29/43

Rekurze faktorizace void factorizeiter ( int x ) { int d = 2; while ( d <= x ) { if ( x % d == 0 ) { printf ( "%d ", d ); x /= d; else d ++; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 30/43

Rekurze faktorizace void factorizerec ( int x ) { int d = 2; while ( d <= x ) { if ( x % d == 0 ) { printf ( "%d ", d ); factorizerec ( x / d ); return; else d ++; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 31/43

Rekurze faktorizace void factorizerec ( int x ) { int d = 2; while ( d <= x ) { if ( x % d == 0 ) { printf ( "%d ", d ); factorizerec ( x / d ); return; else d ++; /* každé volání začíná od d = 2 */ L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 31/43

Rekurze faktorizace void factorizerec2 ( int x, int d ) { if ( d <= x ) { while ( d < x && x % d!= 0 ) d++; printf ( "%d ",d ); factorizerec2 ( x / d, d ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 32/43

Merge sort Jednoduché řadicí algoritmy mají složitost T (n) O(n 2 ). Kvadratická časová složitost je zbytečně velká. Efektivní řadicí algoritmy řadí v čase T (n) O(n log n). Řazení slučováním je takový algoritmus. Algoritmus Merge sort je založen na operaci slučování. Může být popsán rekurzivně: pole o velikosti jednoho prvku je seřazené, když má pole více prvků, rozděĺı se na dvě části (poloviny). merge sort je užit rekurzivně pro seřazení obou částí, obě části se sloučí do většího pole. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 33/43

Merge sort pole: 15 32 74 11 80 10 40 27 34 24 rozdělení: 15 32 74 11 80 10 40 27 34 24 řazení polovin: 11 15 32 74 80 10 24 27 34 40 sloučení: 10 11 15 24 27 32 34 40 74 80 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 34/43

Slučování (merge) Problém: Necht a a b jsou seřazené sekvence. Máme vytvořit seřazenou sekvenci c takovou, že obsahuje všechny prvky z a i z b. Příklad: a: 2 3 6 8 10 34 b: 3 7 12 13 55 c: 2 3 3 6 7 8 10 12 13 34 55 Když a a b jsou pole, můžeme připravit funkci, která je sloučí: void merge ( int *a, int la, int *b, int lb, int *c ); Jak sloučit sekvence? Zkopírovat a do c, přidat b do c a seřadit pole c. Ne to by bylo neefektivní. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 35/43

Slučování efektivně přečíst první prvek obou sekvencí a a b, přidat menší (nebo stejnou) z prvků do c, přečíst další prvek sekvence, ze které bylo přidáno, předchozí kroky opakovat až do vyčerpání jedné ze sekvencí, přidat zbytek nevyčerpané sekvence na konec c. void merge( int *a, int na, int * b, int nb, int *c ) { int ia = 0, ib = 0, ic = 0; while ( ia < na && ib < nb ) if ( a[ia] <= b[ib] ) c[ic++] = a[ia++]; else c[ic++] = b[ib++]; while (ia < na) c[ic++] = a[ia++]; while (ib < nb) c[ic++] = b[ib++]; L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 36/43

Merge sort void merge ( int *src, int *dst, int l, int r, int rend ) { int lend = r - 1, i = l; while ( l <= lend && r <= rend ) dst[i++] = src[l] <= src[r]? src[l++] : src[r++]; while ( l <= lend) dst[i++] = src[l++]; while ( r <= rend) dst[i++] = src[r++]; src dst l r rend L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 37/43

Merge sort (rekurzivně) void mergesortrec( int *a, int *tmp, int l, int r ) { int i, mid; if ( l == r) return; /* 1 prvek -> seřazeno */ mid = ( l + r ) / 2; mergesortrec ( a, tmp, l, mid ); mergesortrec ( a, tmp, mid + 1, r ); merge ( a, tmp, l, mid+1, r ); for ( i = l; i <= r; i ++ ) a[i] = tmp[i]; /* Výsledná řadicí funkce */ void mergesort ( int *a, int n ) { int *tmp = (int*)malloc ( n * sizeof( *tmp ) ); mergesortrec( a, tmp, 0, n-1 ); free ( tmp ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 38/43

Merge sort (iterativně) Rekurzivní funkce merge sort volá sebe sama opakovaně, až je délka seřazeného pole 1. Z tohoto pohledu jsou krátké rozdělené sekvence slučovány do delších. Sekvence jsou sloučeny do pomocného pole, které musí být zkopírováno zpět do zdrojového pole. Iterativní merge sort pracuje zdola nahoru: pole a je rozděleno na sekvence délky 1, sekvence jsou sloučeny do pole tmp (seřazené sekvence 2 prvků), v další iteraci jsou sekvence délky 2 z pole tmp sloučeny do sekvencí v poli a (sekvence 4 seřazených prvků), a znova v další iteraci jsou sekvence délky 4 z a sloučeny do pole tmp (sekvence 8 seřazených prvků), to pokračuje, až je celé pole seřazeno, je-li počet sloučení lichý, je výsledek v poli tmp. V tomto případě je třeba ještě další krok data musí být zkopírována zpět do pole a. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 39/43

Merge sort (iterativně) a 49 62 21 70 89 99 21 76 53 40 87 70 32 70 24 93 90 65 90 tmp 49 62 21 70 89 99 21 76 40 53 70 87 32 70 24 93 65 90 90 a 21 49 62 70 21 76 89 99 40 53 70 87 24 32 70 93 65 90 90 tmp 21 21 49 62 70 76 89 99 24 32 40 53 70 70 87 93 65 90 90 a 21 21 24 32 40 49 53 62 70 70 70 76 87 89 93 99 65 90 90 tmp 21 21 24 32 40 49 53 62 65 70 70 70 76 87 89 90 90 93 99 L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 40/43

Merge sort (iterativně) void mergesort ( int *a, int n ) { int *tmp = (int*)malloc ( n * sizeof(*tmp) ), *src = a, *dst = tmp, *x; int len = 1, last = n-1, i; while ( len < n ) { int l = 0, r = len; while (l <= last ) { merge ( src, dst, l, min(r, n), min(r+len-1, last) ); l += 2 * len; r += 2 * len; len *= 2; x = src; src = dst; dst = x; if ( src!= a ) for ( i = 0; i < n; i ++ ) a[i] = tmp[i]; free ( tmp ); L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 41/43

Merge sort Analýza složitosti algoritmu Merge sort: n Vnitřní cyklus proběhne 2 len krát. Ten volá funkci merge pro vytvoření sekvence délky (nejvíce) len + len. Tudíž, časová n složitost je 2 len 2 len = n, vnější cyklus iteruje, až do hodnoty len == n. Hodnota len se zdvojnásobí v každé iteraci, a tak je počet iterací k, kde 2 k = n, k je tedy k = log 2 (n), případný poslední kopírující krok trvá dobu úměrnou n, celkový čas: T (n) O(n log n + n) O(n log n) Nevýhodou tohoto algoritmu je potřeba pomocného pamět ového prostoru. Jiné řadicí algoritmy (např. Quick sort) toto omezení nemají. L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 42/43

Otázky a odpovědi Otázky... L. Vagner, J. Vogel, ČVUT FIT Rekurze, BI-PA1 43/43