diff --git a/tasks.json b/tasks.json index 540b07f..a7ba93b 100644 --- a/tasks.json +++ b/tasks.json @@ -85,14 +85,16 @@ "id": "26-Z2-2", "type": "open-data", "comment": "SADO - triviální, procházení celých čísel v intervalu a kontrola podmínky", - "requires": [], + "requires": [ + "kucharka-zakladni-algoritmus" + ], "position": [ - 1167.1616821289062, - 208.39923095703125 + -428.70552825927734, + 385.39715576171875 ], "taskReference": "26-Z2-2", "title": "SADO", - "hidden": true, + "hidden": false, "points": 10 }, { @@ -121,8 +123,8 @@ ], "title": "Životně důležitá úloha", "position": [ - -90.7659363746643, - 880.786247253418 + 47.204129695892334, + 886.2504196166992 ], "taskReference": "26-Z2-4", "points": 12 @@ -398,8 +400,8 @@ "taskReference": "27-Z2-3", "requires": [], "position": [ - 809.155632019043, - 1312.5684385299683 + 807.7896041870117, + 1313.9344053268433 ], "title": "Nápis na tričku", "hidden": true, @@ -1178,7 +1180,7 @@ 1192.0383605957031 ], "title": "Vlnění", - "hidden": true, + "hidden": false, "points": 9 }, { @@ -1514,8 +1516,8 @@ "comment": "úloha s odčítáním času", "title": "Zuzka a poník", "position": [ - -1205.247703552246, - -1048.316032409668 + -1190.2212142944336, + -1046.9499435424805 ], "taskReference": "31-Z1-1", "hidden": true, @@ -1885,14 +1887,17 @@ "type": "open-data", "id": "32-Z2-2", "taskReference": "32-Z2-2", - "requires": [], + "requires": [ + "26-Z2-1" + ], "position": [ - 1471.155632019043, - 681.5684385299683 + -73.8359375, + 919.259539604187 ], "title": "Turnaj hada", - "hidden": true, - "points": 10 + "hidden": false, + "points": 10, + "comment": "Dost hustý voser :D" }, { "type": "open-data", @@ -2128,7 +2133,7 @@ "htmlContent": "
Pod tajemným slovem algoritmus se skrývá jen jiný výraz pro postup. Můžete si to představit jako příkaz od maminky „Běž do krámu, kup chleba, a když budou mít měkké rohlíky, tak jich vem tucet“. (A jako slušně vychovaní se tedy vydáte do krámu a koupíte tucet chlebů, protože měli měkké rohlíky :-))
Takovýto příkaz klidně můžeme nazvat algoritmem, ačkoliv to bude asi znít nezvykle – pojem algoritmus se totiž používá hlavně ve světě počítačů. Je to tedy nějaká posloupnost základních příkazů, která řeší nějaký problém. Výběr konkrétního programovacího jazyka rozhoduje o tom, jaké základní příkazy budeme mít k dispozici. Většinou jsou ale skoro stejné.
Mezi základní příkazy patří:
Z těchto základních stavebních kamenů se skládá každý algoritmus. Programem potom rozumíme realizaci algoritmu v nějakém konkrétním programovacím jazyce.
U složitějších programů se pak často setkáme s problémem, že budete mít nějakou posloupnost příkazů, která se bude na spoustě míst programu opakovat, což zbytečně prodlužuje a znepřehledňuje kód.
Řešením tohoto problému je použití funkcí. Funkci si můžeme představit jako nějakou pojmenovanou část programu (s vlastní pamětí), kterou můžeme opakovaně použít tím, že ji v různých částech programu zavoláme. Funkci při zavolání předáme parametry (například seznam čísel), které se dostanou do její vnitřní paměti.
Funkce pak na základě obdržených parametrů může provádět nějaké operace, při kterých pracuje se svojí vnitřní pamětí (mluvíme o lokální paměti, změny v ní se neprojeví nikde mimo funkci). Na konci nám funkce může vrátit nějaký výsledek. Pokud funkce během svého běhu změní i nějaká data v globální paměti, či provede nějakou globální operaci (například výpis textu na monitor), mluvíme pak o funkci s vedlejšími efekty (neboli side-efekty).
Konkrétním příkladem může být funkce, která nám spočítá odmocninu ze zadaného čísla. Ta dostane jako svůj parametr číslo, uvnitř si provede nějaký výpočet, o který se jako uživatel funkce nemusíme starat, a jako výstup nám vrátí spočtenou odmocninu.
", "position": [ -410.8735809326172, - 271.90441131591797 + 273.27046966552734 ] }, { @@ -2262,13 +2267,13 @@ "type": "text", "comment": "https://ksp.mff.cuni.cz/kucharky/zakladni-algoritmy/", "requires": [ - "kucharka-zakladni-algoritmus" + "26-Z2-2" ], "title": "Reprezentace dat", "htmlContent": "Celkem často si v průběhu výpočtu našeho algoritmu potřebujeme pamatovat nějaké hodnoty. K tomu nám programovací jazyky dávají nástroj s názvem proměnná. Ta představuje jakési pojmenované místo v paměti (přihrádku), do kterého si můžeme data ukládat a pak je odtud zase načítat.
Typickým příkladem může být počítání součtu čísel, která nám uživatel zadá na vstupu. Na začátku nejdříve do nějakého místa v paměti uložíme hodnotu 0. Poté postupně, jak nám uživatel zadává čísla, tuto proměnnou pokaždé přečteme, k její hodnotě přičteme nově zadané číslo a výsledek opět uložíme na stejné místo.
Takovéto použití jedné proměnné je velmi jednoduché (tak jednoduché, že ho takto podrobně do řešení KSPčka nepište, není to potřeba), ale také celkem omezené. Co kdybychom si chtěli pamatovat třeba celou zadanou posloupnost čísel? Mohlo by nám stačit vyrobit si spoustu různě pojmenovaných proměnných, ale nejde to lépe? Jde.
Jednotlivé proměnné se mohou kombinovat do složitějších konstrukcí, které obecně nazýváme datovými strukturami. Zkusíme si ty nejzákladnější představit.
", "position": [ - -403.3098793029785, - 475.9121398925781 + -395.09653091430664, + 494.3963623046875 ] }, { @@ -2281,7 +2286,7 @@ "title": "Spojový seznam", "htmlContent": "Pole jsme měli v paměti určené jenom tím, že počítač věděl, kde je jeho začátek a kolik místa v paměti zabírají jeho prvky. Při dotazování na konkrétní index pak podle indexu a podle velikosti prvků počítač přesně věděl, kam do paměti se má podívat, aby našel námi požadovaný prvek (to vše zvládl v konstantním čase). Jednotlivé prvky si tedy vůbec nemusely pamatovat, kde se nachází jejich sousedi, protože všechny prvky seděly v paměti za sebou.
Představme si ale teď situaci, kdy by si každý prvek ještě pamatoval pozice sousedů. Pak bychom mohli mít prvky libovolně rozházené v paměti a jen by se na sebe vzájemně odkazovaly (první prvek by tvrdil, že druhý je na pozici X, druhý by tvrdil, že třetí je na pozici Y, a tak dále).
K lepšímu pochopení tohoto principu je důležité si vysvětlit, co to je ukazatel (nebo také odkaz či anglicky pointer). Každé místo v paměti počítače má své číselné označení, kterému říkáme adresa. Když si vytváříme nějakou pojmenovanou proměnnou, ta se vlastně odkazuje na určité místo v paměti a na tomto místě v paměti je její hodnota.
Co kdyby ale hodnota proměnné byla adresa nějakého jiného místa v paměti? Pak takové proměnné říkáme pointer a umožňuje nám vytvářet výše popsanou strukturu rozházených prvků v paměti.
Spojový seznam je tedy určený svým prvním prvkem (máme v jedné proměnné pointer na tento prvek, který se často nazývá kořen, protože z něj „vyrůstá“ zbytek struktury) a poté u každého dalšího prvku máme za sebou uloženou hodnotu tohoto prvku a odkaz (pointer) na další prvek. Odkazy mezi prvky mohou být i obousměrné, mohou vést dokola (poslední ukazuje na první) či mohou dokonce tvořit nějakou složitější strukturu (pak to ale již nebude čistý spojový seznam).
Pokud pointer nemá nikam ukazovat, realizuje se to odkázáním tohoto pointeru na adresu NULL. To skoro doslovně říká „Neukazuji nikam“.
Co nám takto vystavěná struktura umožňuje v porovnání s polem? Přístup na konkrétní prvek v ní stojí lineárně času, protože ho musíme „odkrokovat“ od prvního prvku (na který máme pointer), tedy musíme udělat až O(N) kroků. Pokud bychom však pointer na daný prvek už nějak měli, samozřejmě na něj můžeme přistoupit v konstantním čase.
Naopak přidávání prvků na konkrétní místo (i jejich odebírání) máme v podstatě zadarmo a spojový seznam můžeme rozšiřovat, dokud na něj máme v počítači paměť. Ve chvíli, kdy chceme přidat nový prvek za prvek, na který máme pointer, jen šikovně přepojíme ukazatele. Pokud předtím ukazatele vedly A→B, teď povedou A →C →B (a při odebírání naopak).
", "position": [ - -1179.2408599853516, + -1177.725112915039, 1064.477451324463 ] },