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