Úvod do programování 10. hodina RNDr. Jan Lánský, Ph.D. Katedra informatiky a matematiky Fakulta ekonomických studií Vysoká škola finanční a správní 2015
Umíme z minulé hodiny Syntax Dvojrozměrné pole Pole polí String.Split() Algoritmy Násobení řádku matice konstantou Prohození dvou řádků matice Součet matic Součin matic ASCII Art Jan Lánský Úvod do programování 10. hodina 2
Cíle hodiny Rekurze Faktoriál Přetečení, nekonečná rekurze Převod z desítkové do dvojkové soustavy Vztah rekurze a cyklu Fibonacciho čísla Verze s dynamickým programováním Binární vyhledávání Hanojské věže Kochova vločka Jan Lánský Úvod do programování 10. hodina 3
Rekurze: Motivace Volání funkce sama sebou jednotlivá volání jsou nazývána zanoření Kratší a přehlednější zdrojové kódy než v případě zápisu bez pomocí rekurze Vystihnutí hlavní myšlenky algoritmu Oproti zápisu bez pomocí rekurze pomalejší běh programu (desítky procent) Při špatném použití i zhoršení časové složitosti řádově Jan Lánský Úvod do programování 10. hodina 4
Pravidla použití rekurze Problém lze rozdělit na menší podproblémy, které vyřeší téže rekurzivní funkce nebo převést problém na menší podproblém Parametr(y) rekurzivní funkce se musí v každém zanoření přibližovat testované hodnotě v ukončovací podmínce Rekurze musí být ukončena podmínkou testující parametr(y) rekurzivní funkce Nepoužívat rekurzi pro kritické systémy (elektrárny, letadla) Jan Lánský Úvod do programování 10. hodina 5
Rekurze: faktoriál Faktoriál n! = 1*2* *(n-1)*n Nerekurzivní řešení: V cyklu násobíme 1 až n Ukončení rekurze n! = n * (n-1)! Rekurzivní volání funkce sama sebe se sníženou hodnotou parametru. Zanoření funkce Jan Lánský Úvod do programování 10. hodina 6
Faktoriál: formálně Faktoriál n je součin čísel od 1 do čísla n n! = 1 * 2 * 3 * * (n-2) * (n-1) * n Faktoriál n-1 je součin čísel od 1 do čísla n - 1 (n-1)! = 1 * 2 * 3 * * (n-2) * (n-1) Faktoriál n vyjádříme pomocí faktoriálu n-1 n! = (n-1)! * n pro n > 1 n! = 1 pro n = 1 a pro n = 0 Tento zápis je rekurzivní Jan Lánský Úvod do programování 10. hodina 7
Rekurze: ladění Funkce je zanořena, breakpoint byl aktivován opakovaně Breakpoint Světlý podklad značí, že tato funkce je vykonávána Jan Lánský Úvod do programování 10. hodina 8
Rekurze: Call stack Zásobník volání funkcí Aktuálně se volá faktoriál s hodnotou parametru 4 Kliknutím na řádek aktivujeme libovolnou funkci a můžeme prohlížet její proměnné Funkce Main Jan Lánský Úvod do programování 10. hodina 9
Faktoriál s výpisy Na začátku funkce napíšeme s jakými parametry je volána Aby šel provést závěrečný výpis, museli jsem zavést proměnnou na výsledek Na konci funkce napíšeme, jaká je její výsledná hodnota Jan Lánský Úvod do programování 10. hodina 10
Faktoriál s výpisy Postupně jsme se zanořovali od 8 do 1 Pro 1 se ukončí rekurze Postupně jsme se vynořovali od 1 do 8 Faktoriál pomocí cyklu počítá obdobně jako vynořování z rekurze Jan Lánský Úvod do programování 10. hodina 11
Součet čísel 1 až n Obdoba faktoriálu Faktoriál je součin čísel od 1 do n. Faktoriál roste velmi rychle, došlo by k přetečení datového typu. Proto ukazujeme součet Kolik je 1+2+ + 10000? Nemuseli jsme nic počítat Sn = n*(n + 1)/2 Jan Lánský Úvod do programování 10. hodina 12
Přetečení zásobníku volání Došla paměť, přetečení zásobníku Jan Lánský Úvod do programování 10. hodina 13
Přetečení zásobníku volání Pokud voláme rekurzivní funkci je nám povolen jen omezený počet zanoření (dle velikosti zásobníku volání) V případě SoucetOd1DoN to bylo 1446 Pro rozumné použití rekurze dostatečně Nejčastěji dojde paměť při nekonečné rekurzi (chybí ukončující podmínka rekurze) Jan Lánský Úvod do programování 10. hodina 14
Konečnost rekurze Rekurzivní funkce musí být vždy zakončena podmínkou pro testování hodnoty parametru funkce Parametr funkce se musí v jednotlivých voláních blížit testované hodnotě Nepřímá rekurze Občas se zapomene ukončit, pak vznikne nekonečná rekurze Rejstřík Cyklus nekonečný Viz Nekonečný cyklus Nekonečný cyklus viz Cyklus nekonečný Faktorial (n) = n * Faktorial (n-1) Faktorial (1) = 1 Jan Lánský Úvod do programování 10. hodina 15
Převod z desítkové do dvojkové soustavy (little endian) Little endian: Postupně vypisujeme zbytky po dělení číslem dvě. Big endian: Obtížnější, museli bychom mít pomocné pole Little endian: Postupně vypisujeme zbytky po dělení číslem dvě. Big endián: Stačí prohodit výpis a rekurzivní volání 13 = 6 * 2 + 1 6 = 3 * 2 + 0 3 = 1 * 2 + 1 1 = 0 * 2 + 1 13 10 1011 2 (little endian) 13 10 1101 2 (big endian) Jan Lánský Úvod do programování 10. hodina 16
Nepoužívat: Uvádíme jen pro ilustraci. Cyklus se vyhodnotí rychleji Převod cyklu na rekurzi Konec rekurze při nesplnění podmínky Voláme rekurzivní funkci s novou hodnotu x Místo cyklu vložíme volání rekurzivní funkce Jan Lánský Úvod do programování 10. hodina 17
Náhrada rekurze Rekurze je silnější prostředek než cyklus náhradu nelze provést automaticky Převod rekurze na cyklus (cykly) Podmínka ukončující rekurzi odpovídá podmínce cyklu Problematický vyšší počet rekurzivních volání (stromová rekurze) Velmi často je nutno použít datovou strukturu zásobník (bude probírána později) Odstranění rekurze použitím jiného algoritmu bude za chvíli Fibonacci Jan Lánský Úvod do programování 10. hodina 18
Odstrašující příklad na rekurzi Řádu 2 Fibonacciho čísla F(0) = 0 Stromová rekurze: Rekurzivní volání je dvakrát F(1) = 1 F(n) = F(n-1) + F(n-2) pro n>1 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, Zlatý řez lim n oo F(n) / F(n-1) = (1+ 5) / 2 = 1.618 Vyskytuje se často v přírodě (listy, ulita) Jan Lánský Úvod do programování 10. hodina 19
Fibonacciho čísla F(0) = 0 F(1) = 1 Fibonacci(20); Fibonacci(30); Fibonacci(50); Proč je tak pomalé? Stromová rekurze Časová složitost O(2 N ) F(n) = F(n-1) + F(n-2) Jan Lánský Úvod do programování 10. hodina 20
Fibonacciho čísla Opakovaně počítáme ty samé hodnoty. Exponenciální časová složitost Kolikrát počítáme F(0) 3x F(1) 5x F(2) 3x F(3) 2x F(4) 1x F(5) 1x 5 4 3 3 2 2 1 2 1 1 0 1 0 1 0 Jan Lánský Úvod do programování 10. hodina 21
Fibonacciho čísla lineárně Časová složitost O(N) Technika Dynamické programování : Rozklad problému na podproblémy a jejich řešení je uloženo pro další použití Postupně počítáme F(0), F(1), F(2) až F(n). Stačí si nám pamatovat dva poslední členy posloupnosti F(n-1) a F(n-2) Jan Lánský Úvod do programování 10. hodina 22
Fibonacciho čísla lineárně Př: červená barva kružnice F(1) a F(2) Červená barva pole F(3) = F(1) + F(2) F(0) F(1) F(2) F(3) F(4) F(5) F(6) F(7) F(8) F(9) 0 1 F(0) F(1) F(2) F(3) F(4) F(5) F(6) F(7) F(1) F(2) F(3) F(4) F(5) F(6) F(7) F(8) 0 1 1 2 3 5 8 13 21 34 Jan Lánský Úvod do programování 10. hodina 23
Binární vyhledávání Hledání prvku v setříděném poli. V 5. přednášce byla probrána nerekurzivní verze Rozpůlíme pole na dvě poloviny, podle hodnoty dělícího prvku se rozhodneme, ve které polovině by se měl prvek nacházet Časová složitost O(log(N)) Rekurzivní verze má také O(log(N)) Jan Lánský Úvod do programování 10. hodina 24
Pole setříděné vzestupně Binární vyhledávání Lineární rekurze, nikdy se neprohledá levá i pravá část najednou Prvek nenalezen, konec rekurze Prvek nalezen, konec rekurze Rekurzivně prohledáme levou nebo pravou část pole dle hodnoty středového prvku Pro rekurzivní verzi potřebujeme znát jakou část pole prohledáváme (začátek, konec) Volání z nerekurzivní funkce: častý trik Jan Lánský Úvod do programování 10. hodina 25
Binární vyhledávání 0 4 9 Hledáme hodnotu 5 v poli p 5 4 5 4 5 6 7 9 0 1 1 2 3 4 5 6 7 9 BVNajdi (p, 6, 6, 5) stred = 6, p[stred] == x BVNajdi (p, 5, 6, 5) stred = 5, p[stred] < x BVNajdi (p, 5, 9, 5) stred = 7, p[stred] > x BVNajdi (p, 0, 9, 5) stred = 4, p[stred] < x Jan Lánský Úvod do programování 10. hodina 26
Hanojské věže Édouard Lucas, 1883 Přemístit všechny disky z věže 1 na věž 3 Lze využít věž 2 Větší disk nesmí být nikdy umístěn na menší disk V každém tahu lze přemísťovat jen jeden nejvrchnější disk 1 2 3 4 5 6 Počet tahů k vyřešení 2 n - 1, kde n > 1 je počet disků https://www.mathsisfun.com/games/towerofhanoi.html Jan Lánský Úvod do programování 10. hodina 27
Hanojské věže 1) Přemístíme n-1 disků z původní věže na pomocnou věž s využitím cílové věže jako odkladiště 2) Přesun největší disk Rozšíření funkce o pomocné proměnné: z původní věž, kam cílová věž, prez pomocná věž jako odkladiště 3) Přesuneme n-1 disků z pomocné věže na cílovou věž s využitím původní věže jako odkladiště Jan Lánský Úvod do programování 10. hodina 28
Hanojské věže pro n = 4 Největší disk 4 Nejmenší disk 1 Vyzkoušejme tento postup v online hře Jan Lánský Úvod do programování 10. hodina 29
Druh fraktálu Kochova vločka Helge von Koch, 1904 Začínáme s trojúhelníkem V každém zanoření z každé hrany odstraníme prostřední třetinu a nahradíme ji dvěma hranami rovnostranného trojúhelníku směřujícími z vločky směrem ven Končíme po zvoleném počtu zanoření http://mathworld.wolfram.com/kochsnowflake.html Zanoření 0 až 3. Vločku tvoří křivka, nikoliv plocha Jan Lánský Úvod do programování 10. hodina 30
Přímá a nepřímá rekurze Přímá rekurzivní volání v těle funkce Všechny probrané příklady Nepřímá rekurzivní volání ve vnořené funkci Př.: Fce1 () { Fce2(); } Fce2 () { Fce1(); } Příklad Rejstřík nekonečná rekurze Smysluplný příklad bude v LS Jan Lánský Úvod do programování 10. hodina 31
Lineární a stromová rekurze Lineární - obvykle jedna větev rekurzivního volání nebo více větví, které se volají s parametrem odpovídajícímu n vydělenému počtem větví. Př.: faktoriál jedna větev [n-1], Př.: binární vyhledávání dvě větve [n/2], ale navíc se vykoná jen jedna Stromová více větví rekurzivních volání, které se volají s parametrem odpovídajícímu původnímu parametru (zmenšenému o konstantou) Př.: Fibonacciho čísla dvě větve [n-1] a [n-2] Př.: Hanojské věže dvě větve [n-1] Jan Lánský Úvod do programování 10. hodina 32
Časová složitost Rekurze je vždy pomalejší než nerekurzivní řešení (desítky procent) Někdy nerekurzivní řešení může mít i lepší asymptotickou složitost (Fibonacci) Lineární rekurze obvykle převod na cyklus O(N), případně O(log(N)) Stromová rekurze hrozí O(2 N ), pokud se volá alespoň dvakrát o délce N Jan Lánský Úvod do programování 10. hodina 33
Zpětná vazba Objevili jste ve slajdech chyby? Včetně pravopisných Nechápete nějaký slajd? Je příliš obtížný, nesrozumitelný? Máte nějaký nápad na vylepšení? Anonymní formulář Odeslání za pár vteřin http://goo.gl/forms/wxkzqbszls Jan Lánský Úvod do programování 10. hodina 34