AII = Algoritmy pro inženýrskou informatiku

Slides:



Advertisements
Podobné prezentace
OZD: Hašování RNDr. Michal Žemlička.
Advertisements

CIT Paměti Díl X.
Stavový prostor. • Existují úlohy, pro které není k dispozici univerzální algoritmus řešení • různé hry • problém batohu, problém obchodního cestujícího.
Orbis pictus 21. století Tato prezentace byla vytvořena v rámci projektu.
HYPERTEXT PREPROCESSOR. PROGRAMOVÁNÍ. DEFINICE POJMŮ Problém Problém nevyřešený, nežádoucí stav obvykle vyžaduje nějaké řešení Neřešitelný problém Neřešitelný.
10. Dynamické datové struktury
Zásobník (LiFo) Fronta (FiFo)
PC SÍTĚ II.
ALGO – Algoritmizace 1. cvičení
Otázky k absolutoriu HW 1 - 5
Programování v C++ Cvičení.
Principy překladačů Mezikód Jakub Yaghob.
Programování PA - 2.
Materiály k přednášce Úvod do programování Ondřej Čepek.
Generování mezikódu Jakub Yaghob
ADT Strom.
Algoritmizace a programování
Algoritmizace a programování Podmíněné a cyklické příkazy- 08
Informatika I 3. přednáška
Jazyk vývojových diagramů
Procedury a funkce Základní charakteristika a použití v programu.
Seminář – Základy programování
Orbis pictus 21. století Tato prezentace byla vytvořena v rámci projektu.
Vyučovací hodina 1 vyučovací hodina: Opakování z minulé hodiny 5 min Nová látka 20 min Procvičení nové látky 15 min Shrnutí 5 min 2 vyučovací hodiny: Opakování.
13AMP 6. přednáška Ing. Martin Molhanec, CSc.. Co jsme se naučili naposled Synchronizace procesů Synchronizace procesů Producent-Konzument Producent-Konzument.
Základy algoritmizace a programování
Orbis pictus 21. století Tato prezentace byla vytvořena v rámci projektu.
 vytváření signálů a jejich interpretace ve formě bitů  přenos bitů po přenosové cestě  definice rozhraní (pro připojení k přenosové cestě)  technická.
Paměťové obvody a vývoj mikroprocesoru
Sémantická analýza Jakub Yaghob
Tato prezentace byla vytvořena
Ethernet Ethernet je jeden z typů lokálních sítí, který realizuje vrstvu síťového rozhraní využívá topologii sběrnice, což znamená že sdílené médium, kde.
Výrok „Počítač je pouze tak inteligentní jako jeho uživatel.“ (Radek Lochman, dnes)
Dělení podle topologie
Simulační modely a programové vybavení. Vývoj simulačních programů  Původně pouze strojový kód –Příliš dlouhé, náročné na programátora, obtížné hledání.
Počítače a programování 1
JavaScript Podmínky, cykly a pole.
3. Příkazy  Příkazy dělíme na jednoduché a strukturované.  Jednoduché příkazy - žádnou jejich dílčí částí neni příkaz - přiřazovací, vstupu a výstupu,
5. Procedury a funkce Procedura je samostatně odladěný algoritmus, v programu může být volána vícekrát. Dvojí terminologie - rozlišujeme procedury a funkce.
1. Derivace Derivace je míra rychlosti změny funkce.
Algoritmizace a programování Binární soubory - 14 Mgr. Josef Nožička IKT Algoritmizace a programování
Orbis pictus 21. století Tato prezentace byla vytvořena v rámci projektu.
STROMY Datová struktura sestávající z uzlů
10. Dynamické proměnné Dynamická proměnná se nezavádí deklarací proměnných, ale vzniká za běhu programu provedením speciálního příkazu. Nemá přidělen žádný.
7. Typ soubor Souborem dat běžně rozumíme uspořádanou množinu dat, uloženou mimo operační paměť počítače (na disku). Pascalský soubor je abstrakcí skutečného.
Gymnázium, Obchodní akademie a Jazyková škola s právem státní jazykové zkoušky Hodonín Počítačové sítě Topologie.
Gymnázium, Obchodní akademie a Jazyková škola s právem státní jazykové zkoušky Hodonín Úvod do programování.
Počítače a programování 1 7.přednáška. Základy Pole ve třídách a metodách Pole Arrays.
Výrok „Vypadá to, že jsme narazili na hranici toho, čeho je možné dosáhnout s počítačovými technologiemi. Člověk by si ale měl dávat pozor na takováto.
Mikroprocesor.
Podprogramy (subroutines) Pojmenované kousky programu, které –tvoří logicky ucelené části –se v programu opakují Jsou zapsány na jednom místě a v případě.
Vrstvy ISO/OSI  Dvě skupiny vrstev  orientované na přenos  fyzická vrstva  linková vrstva  síťová  orientované na aplikace  relační vrstva  prezentační.
Orbis pictus 21. století Tento projekt je spolufinancován Evropským sociálním fondem a státním rozpočtem České republiky Blokové schéma počítače.
Stránkování MATĚJ JURIČIČ 2015/2016 EP1 SPŠ A VOŠ JANA PALACHA KLADNO.
Výukový materiál zpracován v rámci projektu
Inf Cykly ve vývojových diagramech
Programování ENUM, SWITCH,pole jednorozměrná a vícerozměrná, deklarace, inicializace, kopírování, porovnání Erik Král.
Definiční obor a obor hodnot
Jednočipové počítače – instrukční sada
ZAL – 3. cvičení 2016.
Výukový materiál zpracován v rámci projektu
Programovací jazyk C Autorem materiálu a všech jeho částí, není-li uvedeno jinak, je Ing. Jitka Vlčková. Dostupné z Metodického portálu ISSN.
Programování 2. hodina RNDr. Jan Lánský, Ph.D.
Příkazy cyklu (1) Umožňují vícekrát (nebo ani jednou) pro-vést určitý příkaz Jazyk C rozlišuje příkaz cyklu: s podmínkou na začátku: obecný tvar: while.
Dynamické proměnné (1) Proměnné, jejichž počet a (nebo) velikost pa-měti využívané těmito proměnnými se v prů-běhu programu mění Dynamické proměnné lze.
Opakování základních příkazů a syntaxí v programovacím jazyce Pascal
1. ročník oboru Mechanik opravář motorových vozidel
Cyklus for (1) Obecný tvar: for (výraz1; výraz2; výraz3) příkaz
Definiční obory. Množiny řešení. Intervaly.
Algoritmizace a datové struktury (14ASD)
Transkript prezentace:

AII = Algoritmy pro inženýrskou informatiku Přednášky

Členění přednášek první 2-3 přednášky: úplné základy na téma „jak počítá počítač“ další cca 4 přednášky: základy jazyka Pascal (Delphi) celý zbytek semestru: algoritmy

Literatura první téma je popsáno v mnoha menších článcích; souhrnné zpracování neexistuje k výuce Pascalu je vhodná jakákoliv učebnice, na kterou narazíte (ve skriptárně jich je průběžně několik pro FEL, ale vhodné jsou i stredoškolské učebnice apod.).

…pokračování... Pro algoritmy je předepsaná učebnice Wirth, Niklaus: Algoritmy a struktúry údajov, ALFA, Bratislava 1988,9. Ideální, ale drahá a anglická je Cormen, Leiserson, Rivest, Stein: Introduction to Algorithms (je to standardní učebnice US vysokých škol, existuje asi ve 30 různých vydáních).

…pokračování Jako učebnice Delphi jsou vynikající knihy od Marko Cantú: Mastering Delphi 1...7, jejich české překlady jsou Mistrovství v Delphi 1..4, resp. Myslíme v Delphi 6..7. Pozor na Mistrovství v Delphi 5; pod tímto názvem vyšlo v češtině něco jiného (není to moc dobré).

Požadavky Program přednášek, cvičení, otázky ke zkoušce a další informace jsou na http://www.fsid.cvut.cz/cz/U210/aii/ Podmínky pro udělení zápočtu AII: Úspěšné složení nadpoloviční většiny testů, které se budou psát v rámci cvičení z AII.

…pokračování Složení zkoušky z AII: Zkouška má teoretickou a praktickou část, tou druhou začíná v učebně 308. Každý student dostane za úkol vytvořit program, na jeho odladění má jednu až cca pět hodin času. Až se mu to povede, může jej obhájit u ústní části. Navazující teoretická část vychází ze zkouškových otázek z tohoto předmětu.

Základní funkční bloky počítače AB mnoho dalších registrů Periférie PC=čítač instrukcí RAM,ROM PSW, SP, ... 100: 37 101: 02 102: 114 103: 56 R/W střadač (akumulátor) dekodér instrukcí ALU μP DB

…pokračování Základní funkční bloky PC jsou mikroprocesor (μP), paměti (RAM, ROM), periferní zařízení (diskové jednotky, …) navzájem jsou propojeny 3 sběrnicemi: řídicí (zde nakreslen jen vodič R/Ŵ) adresová datová

Adresová sběrnice Jednosměrná: µP -> periférie šířka 32 bitů: adresovací prostor 232 starší mikroprocesory: šířka jen 20 (24) bitů, adresovací prostor 220 = 1MB, zcela nedostačující, nutnost stránkování

Datová sběrnice Obousměrná: µP -> periférie nebo periférie -> µP o směru toku dat rozhoduje vodič R/Ŵ z řídicí sběrnice směr (čtení/zápis) se vždy posuzuje z hlediska mikroprocesoru

Mikroprocesor Obsahuje mnoho specializovaných pamětí (= registrů) pro nás jsou důležité střadač (=akumulátor), čítač instrukcí PC, ukazatel zásobníku SP, stavový registr PSW ALU = aritmeticko-logická jednotka, provádí výpočty (výsledek vesměs ukládá do střadače)

Instrukční cyklus Popis instrukčního cyklu zahájíme tím, že čítač instrukcí (PC) obsahuje adresu instrukce, která se má provést. Tudíž ji je potřeba nejprve načíst do µP (tzv. Fetch) obsah PC se po adresové sběrnici rozšíří do všech periférních zařízení ale adresa je jedinečná, proto jen 1 zařízení na ni může reagovat

…pokračování... Představme si, že adresa je 100 na adresu 100 zareaguje paměť ROM. Zjistí, že na adrese 100 je hodnota 37. Protože vodič R/Ŵ je ve stavu Read, tak paměť hodnotu 37 přiloží na datovou sběrnici. hodnota z paměti se rozšíří po celém počítači, tedy i do µP

…pokračování... Uvnitř µP se hodnota načte do dekodéru instrukcí. dekodér instrukcí má za úkol rozšifrovat, o jakou se jedná instrukci a podle toho zařídit její provedení po vykonání instrukce čítne PC o +1 takže na adresové sběrnici se objeví další adresa (101) a celý proces se opakuje

…pokračování... Většina instrukcí není tak jednoduchých Kdyby se například jednalo o instrukci „přičti číslo, které následuje, k obsahu střadače“, muselo by se nejdříve načíst to „číslo, které následuje“. To znamená, že instrukční cyklus by pokračoval: PC by na adresovou sběrnici přiložil adresu 101,

…pokračování... paměť by na datovou sběrnici přiložila obsah adresy 101, v našem případě číslo 02 číslo 02 by se načetlo do µP. Tentokrát by se ale hodnota nenačetla do dekodéru instrukcí, ale přes přepnutý vnitřní přepínač by se přivedla do ALU ALU provede výpočet. Výsledek většinou zůstane ve střadači

…pokračování Kdyby šlo o instrukci, která má data zapsat do paměti, průběh by byl obdobný až na to, že vodičem R/Ŵ by se namísto čtení z adresy 101 vyvolal zápis do adresy 101 Tedy: program je posloupnost čísel, která mohou znamenat jak data, tak instrukce - rozhoduje o tom pořadí (první je vždycky instrukční kód)

Nepodmíněný skok Popsali jsme si průběh programu, který je zcela lineární. Takový program by nebyl k velkému užitku (nepodmíněné) skoky na jinou adresu se provádějí tak, že se do PC vloží cílová adresa skoku (často načtená z paměti, nebo vypočtená) program pak pokračuje od jiné adresy

Podmíněný skok Důležité jsou skoky podmíněné - provádějí se jen tehdy, když nastaly určité podmínky µP obsahuje sadu jednobitových pamětí, sestavených do registru PSW. Po každé instrukci se v PSW nastaví bity podle výsledku instrukce (např. jestli je výsledek nula, záporný, došlo k přetečení apod.) Podmíněné skoky se provedou, když je v PSW správná hodnota

Volání procedury Často potřebujeme opakovat nějakou část programu - např. výpočet nějaké funkce Jednoduché: na začátek funkce by šlo skočit z mnoha míst Ale problém: jak zajistit, aby skok na konci funkce směřoval, kam má?! 200 hlavní program 900 funkce 980 300

…pokračování... nápad: při skoku na podprogram si poznamenat, odkud se skok prováděl nevýhoda: kdyby si µP poznamenával návratové adresy do registrů, mohl by realizovat jen tolik úrovní podprogramů, kolik by měl registrů  řešení: v paměti RAM vytvořit zásobník (stack), velikost podle potřeby

…pokračování... registr SP (=Stack Pointer) bude vždy ukazovat na vrchol zásobníku na začátku např. SP=2000 zavedeme novou instrukci, volání podprogramu (Call) STACK 2000: 2001: 2002: ... SP 2000

…pokračování... Kdyby v hlavním programu na adrese 200 bylo volání funkce začínající na 900, tak se: návratová adresa (v našem případě 201) zapíše do zásobníku tam, kam ukazuje SP (to znamená na adresu 2000) posune SP tak, aby zase ukazoval na volné místo (tedy na 2001) provede skok na podprogram (v našem příkladu na adresu 900) viz následující obrázek

…pokračování... STACK 2000: 201 2001: 2002: ... SP 2001

…pokračování... Kdyby uvnitř funkce (třeba na adrese 955) bylo další volání (jiného) podprogramu, volání by proběhlo zcela obdobně viz následující obrázek

…pokračování STACK 2000: 201 2001: 956 2002: ... SP 2002

Návrat z podprogramu Na konci podprogramu musí být zvláštní instrukce RET = návrat z podprogramu Operace se zásobníkem se dělají inverzně k volání, to znamená: STACK 2000: 201 2001: 956 2002: ... SP 2001

…pokračování... SP se vrátí na předcházející pozici (je tam číslo 956) obsah té pozice (číslo 956) se načte a použije se jako adresa, na kterou se skočí. Takže skok na adresu 956 znamená, že jsme se vrátili těsně za poslední volání podprogramu

…pokračování až na konci podprogramu (980) najdeme RET, postup se opakuje takže nastane návrat na adresu 201, tedy za volání funkce. STACK 2000: 201 2001: 956 2002: ... SP 2000

Teorie a praxe Popsaný postup byl jen schéma; realita se dost liší: zásobník (stack) roste k menším číslům, t.zn. narůstá nahoru, návratové adresy mají více bytů, a proto zásobník roste/klesá po více bytech, na zásobník se ukládají i četné jiné údaje.

Přerušení Typickým příkladem, kdy se pracuje s přerušením, je obsluha pomalých zařízení (tiskárna) přerušení funguje podobně jako volání procedury; vyvolává se však hardwarově na rozdíl od CALL je přerušení asynchronní, může nastat kdykoliv  musíme se postarat o PSW  na zásobník se ukládá i PSW  pro návrat musí být zvláštní instrukce IRET

Architektura von Neumannova Popsaná architektura počítače se nazývá von Neumannova další klasická architektura je harvardská (má oddělenou sběrnici pro přenos dat od sběrnice, po které se přenáší program – vyšší rychlost, zejména při zpracování dat) moderní vývoj zaměřen na zvyšování výkonu  hlavně víceprocesorová řešení

Víceprocesorové počítače Hlavní problém: i malé množství sekvenčních instrukcí dokáže výrazně zpomalit výpočty Několik koncepcí, např. tatáž instrukce x N procesorů (operují na různých datech) maticové procesory (hyperkrychle) RISC (=Reduced Instruction Set Computer)

RISC zjednodušená sada instrukcí: všechny instrukce mají stejný počet taktů (např. 5) současně se vykonává několik (např. 5 instrukcí), ale každá je v jiné fázi důsledek: během každé fáze se dokončí 1 instrukce načtení instrukce výpočet adresy načtení operandu výpočet zápis výsledku

Základy programového vybavení Program = posloupnost instrukcí a dat, přičemž rozlišit je lze jen pořadím Strojový kód: čitelný pro stroj (ne pro lidi) Jazyk symbolických adres (assembler): zavedení mnemonik (CALL, JMP ADD…). Převod na instrukce 1:1 Vyšší programovací jazyky (FORTRAN, COBOL, LISP, …) - 1 řádek programu se rozvede na N instrukcí

Vyšší programovací jazyky zdrojový text se přeloží do mezijazyku (někdy do assembleru) kompilátor připojí se knihovny sestavovací program (linker) výsledek se dopřeloží do spustitelného kódu (vesměs součást linkeru) zdroje knihovny mezikód program

Interpreter Některé jazyky (BASIC, Pascal) lze vedle kompilace také interpretovat. Interpretace probíhá tak, že se program nepřeloží najednou jako celek, nýbrž řádek po řádku se načte  přeloží  vykoná  zapomene. Výhoda: snadné ladění. Nevýhoda: pomalé

Krize programování Snaha odstranit omezení, která na programátora kladl programovací jazyk: generace jazyků umožňujících "skoro všechno" (Fortran 77, PL/1, ADA) Zklamání: programátor věnuje psaní kódu jen 20% času, pouhých 10% vymýšlení algoritmů a celých 70% ladění !

Jazyky pro usnadnění ladění snaha definovat jazyky, které by automatizovaly ladění: jazyky 3. generace (PASCAL, ...) Problém: tyto jazyky nepodporují spolupráci více programátorů na jednom projektu !

…pokračování Jak toho dosáhnout: maximum chyb odhalit už při kompilaci (zejména syntaktické chyby) za chodu programu sledovat podezřelé anomálie, které mohou signalizovat chybu Tímto postupem nelze odhalit algoritmické chyby !

PASCAL a snadné ladění Silná typová kontrola nelze použít nic, co nebylo předem deklarováno kontrola správnosti typů při přiřazení apod. kontrola rozsahů a typů za běhu programu „znechucování“ programátorů typický příklad: GOTO strukturované programování

Strukturované programování Programy nelze zkoumat odděleně od dat, nad kterými operují; nutno je chápat v jednotě Programy i data postupně dekomponovat na jednodušší části Důsledek: pro programy používat strukturované příkazy

Strukturované příkazy Strukturovaný příkaz má 1 vstup a 1 výstup Důsledek: příkazy a skupiny příkazů lze do sebe hierarchicky vnořovat Nestrukturované příkazy (jako je IF) nutno upravit

Další vývoj? Objektově orientované programování (viz předmět OOP) Deklarativní programování (neříkáme počítači, jak to má udělat, ale jen co se má udělat) Grafické programovací rozhraní, programování příkladem, umělá inteligence...

Lokální sítě Omezení na pozemek jednoho majitele (právní důvody) Celková délka kabelů do stovek metrů Přenosová rychlost (teoreticky) 10, 100 nebo 1000 Mbit/s Médium: optické kabely, metalické kabely (dříve koaxiální, dnes téměř výlučně symetrický kabelový pár = twist)

Síť s kruhovou topologií stanice A stanice D stanice B monitorovací uzel stanice C

…pokračování... Když stanice A chce poslat zprávu stanici C, rozdělí ji do rámců (paketů): adresa odesílatele adresa příjemce služební údaje DATA zabezpečení

…pokračování... Existují 2 technologie: buď existuje konstantní počet rámců (shodný s počtem stanic), nebo se rámce vkládají stanice A odešle svůj rámec po směru sítě, tzn. stanici B. ta zjistí, že rámec není určen pro ni  beze změny ho pošle dál

…pokračování... Až stanice C zjistí, že rámec je její a načte si její obsah ve služebních údajích nastaví bit, že si zprávu načetla. Pak paket pošle dál až paket dorazí zpátky k odesílateli A, je to pro něj potvrzením o správném doručení odesílatel A paket stáhne z oběhu

…pokračování... Problém: co když vlivem nějaké poruchy dojde k poškození adresy odesílatele? vznikne bludný paket, který bloudí sítí věčně, zdržuje a nikdo ho nezruší proti bludným paketům se zavede monitorovací uzel, který si každý paket při průchodu označí. Jakmile nějaký paket projde monitorovacím uzlem 2x, je bludný a je vyřazen.

…pokračování... Typické pro optická vlákna (protože jsou jednosměrná) známý obchodní produkt: Token Ring® (IBM) největší nevýhoda: citlivost na poruchy (možnost zlepšit zdvojením, viz následující obrázek)

Dvojitá kruhová síť stanice A stanice D stanice B stanice C

…pokračování... Když v některé sekci dojde k poruše, přilehlé stanice automaticky sekci vyřadí a síť je schopna pokračovat v práci viz obrázek na následující straně

…pokračování stanice A stanice D stanice B stanice C

Topologie sběrnice/hvězda Typické pro sítě s protokolem CSMA/CD (Carrier Sense, Multiple Access/Collision Detection): Mnohonásobný náhodný přístup s detekcí nosné a řešením kolize Obchodní produkt: Ethernet® stanice A stanice B stanice Z

CSMA/CD Zajímavost: poprvé navržena pro rádiovou síť Aloha (universita na Havaji) přenos probíhá v paketech síť nemá žádný řídicí člen (supervizora), což je důležité z hlediska spolehlivosti (vojenství)

…pokračování... Když stanice A chce odeslat paket, nejprve si poslechne, jestli na lince neběží nějaký přenos (= detekce nosné) pokud zjistí cizí přenos, počká náhodnou dobu a pokusí se znovu pokud nezjistí žádný přenos, začne vysílat svůj paket (= mnohonásobný náhodný přístup)

…pokračování... kdyby se stalo, že několik stanic začne vysílat současně, jde o kolizi. jak k ní může dojít? Vlivem konečné rychlosti šíření signálu! To je také jeden z důvodů, proč je omezena fyzická délka kabelů. stanice kolizi zjistí tak, že současně s vysíláním poslouchá a kontroluje, jestli přijala totéž, co vyslala

…pokračování... při zjištění kolize okamžitě přestane vysílat (resp. ještě odešle signál JAM), počká náhodnou dobu a pokusí se o odeslání paketu později dojde-li k poškození paketu přenosem, zjistí se to pomocí CRC a paket se zahodí o to, aby došly všechny pakety (a ve správném pořadí), se starají vyšší vrstvy podle modelu ISO/OSI

Zahlcení sítě Když budeme zvyšovat požadavky na přenos, roste pravděpodobnost kolize. Protože při kolizi se nic nepřeneslo, každá kolize současně zvyšuje požadavky na přenos! V krajním případě může dojít k zahlcení sítě, při kterém se už pro samé kolize nepřenese vůbec nic!

…pokračování... teorie skutečně přenesené pakety bod zahlcení požadavky na přenos

…pokračování Proti zahlcení se bojuje tím, že časy, po které stanice při kolizi čeká, jsou sice náhodné - ale při rostoucí hustotě kolizí se protahují. Tak se sice zhoršuje propustnost sítě, ale nikdy nedojde k zablokování (modrá křivka)

PASCAL - základy syntaxe slova se oddělují mezerou, tabulátorem, CR/LF nebo komentářem komentář: závorky { xxx } nebo (* xxx *) nebo od // až do konce řádku kompilátor NEROZLIŠUJE malá/velká písmena (samozřejmě mimo stringy) příkazy se oddělují středníkem

Zlaté pravidlo o střednících Kdykoliv jste na rozpacích, zda máte či nemáte napsat středník, použijte ho. Jediné 3 případy, kdy se středník napsat nesmí: mezi IF…THEN…ELSE za RECORD (nebo CLASS) za koncem programu ( je tam END. )

Typický „zubatý“ tvar BEGIN WRITE (‘Toto je příklad‘); IF X<10 THEN Y := X + 1; WRITE (‘Je menší‘); END;

Identifikátory Jakákoliv kombinace písmen a číslic, začínající písmenem (znak __ se bere jako písmeno), ale nelze používat diakritiku rozlišuje se jen prvních 64 znaků doporučeno: používat DLOUHÉ identifikátory a kombinovat malá/velká písmena: PlatZaUcetniObdobi

Blok LABEL { deklarace návěští } CONST {deklarace konstant } TYPE {deklarace typů } VAR {deklarace proměnných } PROCEDURE x FUNCTION BEGIN { tělo = výkonná část bloku } END deklarativní procedurální

Struktura programu PROGRAM JmenoProgramu; tečka BLOK

Procedury a funkce PROCEDURE NazevProcedury (Parametry); FUNCTION NazevFunkce (Parametry) : Typ; BLOK BLOK

Konstanty Pi = 3.141; DlouheCislo = 245897556174; Sestnactkove = $FF08; JedenZnak = ‘@’; JinyZnak = Chr(64); // nebo #$40, #64 Retezec = ‘Toto je text’; JinyRetezec = ‘Řetězec na tři’#13#10 + ‘řádky‘^M‘pod sebou‘;

Typy: klasifikace Obecné schéma: IdentifikátorTypu = Specifikace; Typy jsou: jednoduché strukturované pointer

Jednoduché typy ordinální typy reálné typy INTEGER 4 byty, +231-1 a četné odvozené CHAR ASCII, nebo WideChar…UNICODE BOOLEAN jen TRUE nebo FALSE výčtový typ (Karel, Jan, Martin, Lojza) interval 7..26 reálné typy REAL 8 bytů a četné odvozené

Integer

Real

Příklady jednoduchých typů TDenTydne = (Pondeli, Utery, Streda, Ctvrtek, Patek, Sobota, Nedele); // výčet TPracDen = Pondeli..Patek; // výčet TVelkaPismena = ‘A‘..‘Z‘; // interval

Strukturované typy Pole Záznam (record) Množina Soubor ARRAY [index] OF TypPrvků Záznam (record) RECORD Množina SET OF BázovýTyp Soubor TEXTFILE // textový FILE OF TypPrvků // binární s typy FILE // binární, netypový

Příklady polí ARRAY [Pondeli..Patek] of Real; ARRAY [Char] OF 1..1024; ARRAY [1..5] OF ARRAY [-6..30] OF ARRAY [1..2] OF Byte; ARRAY [1..5, -6..30, 1..2] OF Byte;

String = zvláštní případ pole slouží k práci s texty, a to buď v ASCII nebo v UNICODE mnoho stringových procedur a funkcí prakticky neomezená délka Délka, ReferenceCount a nulový byte stringy v Delphi jsou plně kompatibilní s typem PChar 1=RefC 4=délka A H O J ? ?

Záznam TZaznam = RECORD A : Real; B, C : Byte; S : String; R : ARRAY [1..5] OF Real; END;

Reference na položky záznamu Reference se dělá pomocí „tečkové notace“: nejdříve v části TYPE nadeklarujeme typ (např. typ Tzaznam podle předch. obrázku) pak v části VAR vytvoříme proměnnou toho typu, např. Zaznam : TZaznam;

…pokračování pak se lze odvolávat na jednotlivé části záznamu například takto: Zaznam.A …reálné číslo Zaznam.C …byte Zaznam.S …string Zaznam.S [6] …6-tý znak stringu Zaznam.R [3] …3-tí reálné číslo pole R bez mezer kolem tečky !

Záznam s variantní částí Začátek je konstantní data od CASE až na konec jsou variantní. Selektor záznamu je nepovinný; není-li uveden, musí se jednotlivé identifikátory lišit tak, aby je kompilátor rozpoznal (pozor, v Delphi se musí lišit vždy!)

Příklad záznamu s variantní částí TZv = RECORD Cena : Real; CASE D: TDenTydne OF Pondeli: (A : real; B : Byte); Utery ..Nedele: (X : Integer); END;

(doplněno podle nejdelší varianty) …vysvětlení se selektorem záznamu: bez selektoru záznamu Cena Cena Cena Cena D D A X A X B B volné místo (doplněno podle nejdelší varianty)

Množina Smyslem množiny je, zpřístupnit rychlé strojové instrukce AND, OR apod. Bázový typ množiny musí být ordinální Z praktických důvodů smí mít nejvýše 256 prvků (= 32 bytů) Množina se vytváří konstruktorem množiny

Příklad práce s množinou TYPE TMnozina = SET OF CHAR; VAR X, Y : TMnozina; BEGIN X := [‘A‘,‘B‘,‘X‘]; Y := X + [‘Y‘]; // sjednocení X := Y * [‘A‘, ‘Y‘, ‘C‘]; // průnik IF ‘X‘ IN Y …. // je prvkem ?

Soubory textový soubor: čitelný pro člověka (číslo je ve formě jednotlivých číslic, jako když se tiskne na tiskárně), ale kvůli nutným konverzím pomalé binární soubor: čísla jsou ve stejném formátu jako v paměti binární soubor je typový (všechny záznamy musejí být stejného typu) nebo netypový (není určeno, jaká data obsahuje) v OOP je zvláštní případ Stream

Příklad čtení textového souboru VAR F : TextFile; S : string; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Reset (F); Read (F, R); ReadLn (F, S); ReadLn (F, R, S); CloseFile (F); neuvedeme-li F, čte se z klávesnice stringy se mohou číst jedině pomocí ReadLn

Zápis do textového souboru VAR F : TextFile; S : string; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Rewrite (F); // nebo Append (F); Write (F, 3.141); //přípustné formátové specifikace R:7:2 WriteLn (F, R, ‘je výsledek‘); CloseFile (F);

Čtení typ. binárního souboru VAR F : FILE OF Real; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Reset (F); Read (F, R); Seek (F, 23); // současně otevře i pro zápis Read (F, R); // ReadLn samozřejmě nelze CloseFile (F);

Zápis do typ. binárního souboru VAR F : FILE OF Real; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Rewrite (F); // nebo Append (F); Write (F, 3.141); Seek (F, 23); // současně otevře i pro čtení Write (F, R); // nebo Read (F, R); CloseFile (F);

Čtení z netypového souboru VAR F : FILE; i, j : Integer; Buf : ARRAY[0..1023] OF Byte; BEGIN AssignFile (F, ‘C:\Data.TXT‘); Reset (F, 1); i := SizeOf (Buf); BlockRead (F, Buf, i, j); IF i<>j THEN Chyba…. počet bytů na 1 položku poslední parametr je nepovinný

Proměnné zapisují se v části VAR obecné schéma: Identifikátor : TypProměnné; teprve zápisem v části VAR se proměnným přiděluje místo v paměti. Mluvíme o statické alokaci paměti. Později uvidíme, že existuje i dynamická alokace.

Typované konstanty Chovají se jako proměnné, ve starších verzích do nich šlo i přiřazovat hodnotu (dnes o tom rozhoduje direktiva kompilatoru) Jedinou podstatnou změnou je, že mají definovanou počáteční hodnotu. Poznámka: typované konstanty si každý procvičí individuálně; na cvičení se budou probírat složitější případy!

…pokračování... CONST Maximum: word = 26; Pomer: real = -0.723; Adresa : pointer = Addr(X); { musím znát při kompilaci } Zahlavi: string[5]='Sekce'; Pravda : string[9] ='Ano';

…pokračování... Typovanými konstantami mohou být též pole a recordy, což je velmi výhodné, protože se do nich jednoduše zadávají počáteční hodnoty. Vektory pole se uzavřou do závorky:

…pokračování... TYPE TPole = ARRAY[1..3] OF string[20]; CONST Pole : TPole = ( 'Jedna', 'Dva', 'Tri' ); Digits: ARRAY [1..9] OF Char = ('1','2','3','4','5','6','7','8','9');

…pokračování... Digits : ARRAY [1..9] OF Char { dovolené zjednodušení } = '123456789';

…pokračování... Pokud je pole vícerozměrné, musí se do závorek uzavřít vektory v každé dimenzi: TYPE TMatrix = ARRAY [0..1, 0..1] OF integer; CONST Matrix : TMatrix = ( (1,2), (3,4) );

…pokračování... Podobně (tzn. v závorkách) se dají zadat též položky recordu. U recordů se navíc musí udat též jméno položky (zakončené dvojtečkou) a položky se musí zapsat v přesně stejném pořadí jako byly deklarovány. Příklad:

…pokračování... TYPE TBod = RECORD X,Y : real END; CONST Bod : TBod

…pokračování... Podobná pravidla platí i pro množiny, které se konstruují uzavřením do hranatých závorek: TYPE TDig = SET OF 0..9; CONST Licha : TDig = [1,3,5,7,9];

…pokračování... Ale pozor, nefunguje použití typované konstanty jako dimenze pole. Potřebujeme-li vytvořit dynamická pole, musíme postupovat jinak (viz dynamická a otevřená pole).

…pokračování Toto nefunguje! CONST Mez : integer = 28; TYPE Pole = ARRAY[1..Mez] OF ...

Deklarace procedur a funkcí Procedury + funkce = Metody procedury nevracejí hodnotu funkce vracejí hodnotu (libovolného typu) deklarace používá formální parametry, které jsou při volání nahrazeny skutečnými parametry (samozřejmě musejí být kompatibilního typu)

Hlavička a tělo metod Existují dvě varianty: tělo procedury = blok hlavička metody, za ní bezprostředně tělo nebo hlavička (tzv. forward = dopředná deklarace) a na jiném místě hlavička i tělo; v takovém případě by obě hlavičky měly být totožné (používejte Ctrl+Shift+C) tělo procedury = blok všechny deklarace jsou v bloku lokální po opuštění bloku ztrácejí proměnné hodnotu

Příklady hlaviček PROCEDURE Jedna ( S:string; P,Q:real ); FUNCTION Dva : TDenTydne; FUNCTION Tri ( var A:byte; B:char ) : real; PROCEDURE Ctyri ( const X: string ); pokud se použijí specifikátory (override, overload, abstract, virtual, dynamic apod.), tak se píší jen za první hlavičku a oddělují se středníkem

Parametry metod není-li uvedeno jinak, předávají se hodnotou. Hodnota parametru se zkopíruje na zásobník, kde s ní metoda pracuje a po ukončení metody se ta hodnota zapomene uvnitř metody můžeme s hodnotou libovolně pracovat a měnit ji žádná změna parametru se nepřenese ven z metody

Parametry volané odkazem parametry označené jako VAR se předávají odkazem = jménem = adresou. Metodě se nepředává parametr jako takový, ale jen adresa jeho umístění v paměti. změny VAR parametrů jsou trvalé (je to způsob, jak exportovat výsledky ven) jako skutečné parametry lze za VAR parametry dosadit jen to, co má adresu (příklad: 3*(N+1) je špatně !!)

Příkazy všechny příkazy jazyka Pascal (Delphi) jsou strukturované

Příkazy: klasifikace... Jednoduchý příkaz přiřazovací := bez mezer! volání procedury/funkce použitím jména a skutečných parametrů skok GOTO prázdný ; ;

…pokračování Strukturované příkazy složený BEGIN … END podmíněný IF CASE cyklu FOR WHILE REPEAT with WITH

Přiřazovací příkaz dvojznak := bez mezery přiřazovat lze jen výrazy slučitelného (kompatibilního typu). Nemusejí to být jen totožné typy (může např. jít o 2 intervaly na tomtéž hostitelském typu apod.) přiřazuje se celá proměnná, i strukturovaná (tzn. netřeba přiřazovat po položkách). Výjimka u pointerů (stringy, objekty, …)

Konverze při přiřazení při přiřazení se automaticky provádí konverze INTEGER  REAL obráceným směrem to nejde !

Příkaz (volání) procedury, funkce zapíše se její jméno se skutečnými parametry (musejí být správného typu) srovnejte deklaraci: FUNCTION Xyz (A:integer; B,C: real) : string; a volání této funkce: S := Xyz ( N, 123, N*(22/7) ); Poznámka: další podrobnosti probereme později

Některé standardní procedury Rewrite (F) otevře soubor pro zápis Append (F) dtto, ale za koncem Reset (F) otevře soubor pro čtení Read (F, Var1, Var2, …) načtení Write (F, Var1, Var2, …) zápis CloseFile (F) zavření souboru

Některé standardní funkce... ABS (X) absolutní hodnota SQR (X) X2 SQRT (X) odmocnina SIN (X), COS (X), ARCTAN (X) EXP (X) eX LN (X) lneX TRUNC (X) odsekávání 3.7  3 ROUND (X) zaokrouhlení 3.7  4

…pokračování... ORD (X) ordinální číslo CHR (X) ASCII znak číslo X SUCC (X) následovník PRED (X) předchůdce ODD (X) True, je-li X liché EOF (F) True, je-li konec souboru F EOLN (F) True na konci řádky textového souboru

…pokračování... FORMAT (‘Toto je %d. formátovací %s‘, [ 1, ‘string‘] ); IntToStr (123); StrToInt (‘000321’); DateTimeToStr (Now); ShowMessage (‘Stiskni tlačítko‘); k := MessageDlg (‘Toto se vypíše‘, mtError, [mbYes, mbNo], 123456);

Příkaz GOTO není strukturovaný, ale je užitečný aby se omezilo riziko jeho použití, jsou uměle kladeny překážky nutno definovat návěští (v části LABEL) návěštím nutno označit cíl skoku skok lze provádět jen uvnitř jednoho bloku!

Příklad GOTO LABEL CilSkoku; BEGIN ……. CilSkoku: GOTO CilSkoku;

Prázdný příkaz neobsahuje nic: ; ; ; ; ; ; má hlavně syntaktický význam: způsobuje, že středník můžeme napsat skoro všude (je-li tam nadbytečný, pokládá se za prázdný příkaz)

Priorita operátorů NOT * + = / - <> DIV OR < <= / - <> DIV OR < <= MOD XOR > >= AND

středník zde být nemusí, Složený příkaz „programové závorky“ BEGIN P1; P2; … Pn END středník zde být nemusí, ale může

Podmíněný příkaz IF IF podmínka THEN příkaz1 IF podmínka THEN příkaz1 ELSE příkaz2 zde nesmí být středník ! podmínka příkaz1 příkaz2

Podmíněný příkaz CASE... příkaz1 příkaz2 příkazN

za zapomenutí tohoto END …pokračování CASE... CASE proměnná OF Varianta1: Příkaz1; Varianta2: Příkaz2; ….. VariantaN: PříkazN; END; za zapomenutí tohoto END se vyhazuje od zkoušky !

…pokračování CASE Obvyklá rozšíření: 3..5 3, 4, 5 interval namísto výčtu variant: 3..5 3, 4, 5 ELSE pro neuvedené varianty nejsou zde žádné BREAKy apod. provede se jen správný příkaz (v Pascalu jsou příkazy paralelně, v C sériově!)

Cyklus FOR FOR i := Pondeli TO Nedele DO příkaz; FOR i := Nedele DOWNTO Pondeli DO příkaz pracuje s jakýmkoliv ordinálním typem krok může být jen +1 nebo -1 řídicí proměnná cyklu musí být z téhož bloku! příkaz smí být jen jeden (BEGIN…END)

Cyklus WHILE - +

WHILE - pokračování WHILE podmínka DO příkaz; test je před příkazem, tedy příkaz nemusí proběhnout ani jednou ! cyklus ukončí hodnota False příkaz je jen jeden (složený BEGIN…END)

Cyklus REPEAT - +

REPEAT - pokračování oproti WHILE je „všecho naopak“: test je až na konci, proto příkazy vždy proběhnou aspoň jednou příkazů může být více cyklus se ukončí hodnotou True typické pro nekonečný cyklus: REPEAT ….. UNTIL False

Nestandardní rozšíření cyklů BREAK - vyskočí se z cyklu ven a pokračuje se příkazem následujícím za cyklem CONTINUE - zvětší/zmenší se řídicí proměnná a zahájí se nový průchod cyklem EXIT (platí obecně, ne jen v cyklech) - vyskočí se ven z procedury/funkce

Příkaz WITH není to výkonný příkaz - nedělá nic viditelného usnadňuje programování při práci s recordy (jde o jakési „vytknutí před závorku“) doporučuje se hojně používat, protože kompilátor s ním vytváří optimalizovaný kód

Příklad WITH WITH Xyz DO BEGIN A := … D := B + Cena; END; proměnná Xyz WITH Xyz DO BEGIN A := … D := B + Cena; END; namísto Xyz.A.. Xyz.D.. apod Cena D A B

Oblast platnosti identifikátorů identifikátor platí uvnitř bloku, ve kterém je definován sejdou-li se 2 stejné identifikátory, které označují každý něco jiného, tak má přednost ten lokálnější, tzn. vnitřnější. Méně lokální identifikátor jím je přemaskován, tedy není přístupný, ale nepřestal existovat!

Příklad kolize identifikátorů zde platí A, B a A, Z, ale nelze použít A , protože je přemaskován A VAR A, B VAR A, Z zde platí A, B a neplatí A, Z

ALGORITMY a datové struktury

Základní pravidlo OOP Algoritmy a data nikdy nelze posuzovat odděleně, ale vždy jen ve vzájemné jednotě a souvislostech.

Zásobník LIFO=Last In, First Out Realizace #1: pomocí pole potřebujeme pole P a ukazatel Z na vrchol zásobníku (= index prvního volného místa) 3 2 1

… pokračování Zásobníku ... při zápisu se zapíše na (volné) místo, kam ukazuje Z, pak se Z posune na další volné místo. Přitom nutno kontrolovat přetečení, tzn. aby Z nepřesáhlo meze pole. při čtení se nejdřív Z o jednu pozici vrátí a potom se čte z místa, kam ukazuje Z. Přitom nutno kontrolovat podtečení (tzn. aby Z nebylo menší než nejmenší index pole, čili aby se nečetlo, co ještě nebylo zapsáno)

…pokračování Zásobníku Realizace #2: pomocí spojového seznamu (viz později) - jedná se o jeho zvláštní případ zvlášť výhodné je použití zásobníku při rekurzi

Fronta FIFO=First In, First Out Realizace #1: pomocí pole potřebujeme pole P, ukazatel Z na začátek fronty a ukazatel K na konec fronty 3 2 1

…pokračování Fronty... A H O J K Z při zápisu se zapíše na (volné) místo, kam ukazuje Z, pak se Z posune na další volné místo při čtení se čte z místa, kam ukazuje K a po přečtení se K posune na další pozici. Přitom nutno kontrolovat podtečení (tzn. aby K nepředběhlo nebo nebylo rovno Z)

… pokračování Fronty Realizace #2: pomocí spojového seznamu (viz později) - jedná se o jeho zvláštní případ typické použití: klávesnice, tiskárna, … problém: pole by mělo být „nekonečné“ řešení: kruhová fronta

Kruhová fronta dosahuje se funkcí modulo: i:=(i+1) mod N je nutno také kontrolovat přetečení, tzn. aby začátek nedosáhl či nepřepsal konec

statické proměnné (VAR) Volná paměť = heap program statické proměnné (VAR) zásobník volná paměť Motivace: dosavadní prostředky neumožnily dynamicky měnit velikost nebo počet proměnných Volná paměť (Heap) se využívá jako prostor, ve kterém si můžeme dočasně vytvářet (a rušit) proměnné

Dynamické proměnné Proměnné, které dynamicky (=za chodu a podle potřeby) vytváříme ve volné paměti se nazývají dynamické proměnné na začátku nepotřebujeme znát jejich počet podstatný rozdíl: ke statickým proměnným (VAR) přistupujeme přes jejich název (identifikátor) dynamické proměnné název nemají (a mít nemohou): jak se k nim přistupuje ?!

Pojem POINTERu Pointer = směrník = ukazatel vypovídá o proměnné 2 věci: kde se proměnná v paměti nachází, tzn. určuje její adresu jak se s proměnnou má pracovat, tzn. definuje její typ z hlediska syntaxe se chová jako každá jiná proměnná

Příklady deklarace pointeru... VAR JmenoPromenne : ^NejakyTyp; nebo JinaPromenna : JmenoTypuPointer; pokud jsme ovšem předtím provedli TYPE JmenoTypuPointer = ^NejakyTyp;

…a vysvětlení VAR P : ^real; tím jsme vytvořili statickou proměnnou P, dlouhou 4 byty. Ta (po přiřazení) bude obsahovat adresu do paměti (typicky do heapu) a na této adrese bude ležet proměnná typu real paměť statických proměnných P 4567 obsah= 3.141 adresa= 4567 volná paměť (heap)

Konvence Podobně jako je zvykem, všechny typy označovat pomocí identifikátorů začínajících na T…, tak je zvykem pointery označovat pomocí identifikátorů začínajících na P…, např. type PReal = ^real; PPrvek = ^TPrvek;

Pomůcka stříška ^ před typem proměnné je zdegenerovaná šipka , která bývala na starých klávesnicích nad klávesou 6 to mělo mnemotechnický význam: P : ^real se dá číst jako „P je typu šipka (=ukazatel) na real“

New pro práci s dynamickou pamětí existuje procedura New. Jako parametr se jí předá pointer New z volné paměti „vyřízne“ tolik bytů, kolik je potřeba (podle toho, na jaký typ ten pointer ukazuje) a do pointeru přiřadí adresu té „vyříznuté“ paměti

Příklad New var P: ^real; begin New (P); ve volné paměti se našlo místo pro 1 číslo real, na které ukazuje P P volná paměť (heap)

Poznámky kolik místa se má z volné paměti „vyříznout“, pozná New podle toho, na jaký typ P ukazuje obsah paměti, na kterou P po provedení New(P) ukazuje, není definován. Proto je třeba přiřadit si do P^ nějakou hodnotu, než budeme P^ používat

Dispose Obrácenou operací k New je Dispose Za to, aby se nepotřebné dynamické proměnné vrátily systému pomocí Dispose, je odpovědný programátor ! Proceduře Dispose se jako parametr předá pointer na dynamickou proměnnou. Dispose „přilepí“ místo zabrané tou proměnnou zpátky k volné paměti POZOR, Dispose hodnotu P nezmění, ale P^ se pak už nesmí používat !

Fragmentace paměti Nevýhodou častého používání New a Dispose je, že časem dojde ke fragmentaci paměti. Některé jazyky používají Garbagge Collector (obdoba Defrag pro paměť), ale Delphi nikoliv (problém real-time aplikací) Pascal používá jiný mechanismus v Delphi se objektový mechanismus stará o správné Dispose nepotřebných objektů

Dereference... Základní operace s pointerem se nazývá dereference. Je to nalezení proměnné, na kterou pointer ukazuje. Označuje se šipkou (resp. stříškou): Je-li P : ^real; tak P^ je dereference která znamená „vyjdi z P, jdi po směru šipky a pracuj s proměnnou, na kterou tam narazíš“

…vysvětlení P^ tedy znamená proměnnou (v našem případě typu real) velký praktický význam mají pointery na typ record 4567 proměnná real adresa= 4567 volná paměť (heap)

funkce Addr (X) Rozšíření jazyka: funkce P := Addr (X); vrací adresu (pointer) na libovolnou proměnnou X. Platí i na statické proměnné. Tato funkce je kompatibilní s libovolným pointerem. Platí rovnost Addr(X)^ = X v Delphi má stejný význam i operátor @ (nedoporučuji používat)

Pointer a položky recordu Pokud P je pointer na nějaký record, pak P^ znamená celý tento record. Na jednotlivé položky recordu se můžeme odvolávat běžným způsobem pomocí „tečkové notace“. Například: P^.Jmeno …v recordu, na který ukazuje pointer P, bereme položku Jmeno (string) P^.Dalsi …v recordu, na který ukazuje pointer P, bereme položku Dalsi (pointer)

Přiřazování pointerů Z hlediska syntaxe je pointer proměnná jako každá jiná; lze ji tedy i přiřazovat (při zachování pravidel kompatibility; přiřazovat lze mezi ukazateli na kompatibilní typy) NIL je kompatibilní s jakýmkoliv pointerem a znamená „pointer který neukazuje nikam“ (má hodnotu $0000) typ POINTER je kompatibilní s jakýmkoliv ukazatelem (ale nelze ho dereferovat!)

Pozor při přiřazování Při přiřazování pointerů je třeba dávat pozor: existují 2 různé zápisy; oba jsou správné, ale každý znamená něco jiného! varianta 1 P := Q; varianta 2 P^ := Q^;

Přiřazení 1 P P := Q; způsobí, že P bude ukazovat na stejnou adresu jako Q tedy P^ i Q^ dá stejnou hodnotu 5678 ale P i Q teď ukazují na tutéž proměnnou k proměnné „1234“ se ztratil přístup (!) 1234 Q 5678 P 1234 Q 5678

Přiřazení 2 P^ := Q^; způsobí, že do P^ se zkopíruje obsah Q^ tedy P^ i Q^ dá stejnou hodnotu 5678 P i Q pořád jsou různé proměnné 1234 Q 5678 P 5678 Q 5678

Použití pointerů v recordech Typické je používání pointerů v recordech: TPrvek = record Jmeno: string; Plat: real; … Další: ^TPrvek; end; Výjimka z pravidla, že identifikátor musí být dřív nadeklarován, než je použit (Zdůvodnění: každý pointer má délku 4 byty, lze mu tudíž přiřadit místo v paměti)

Vysvětlení... Položka Další v recordu TPrvek na první pohled vypadá, jako by šlo o nějakou divnou rekurzi, při které record ukazuje sám na sebe  Tak to ale vůbec není! Další neukazuje na stejný record, nýbrž na jiný record stejného typu

… vysvětlení prostřednictvím položky „Další“ se recordy mohou navzájem různě propojovat typické použití: seznamy Jméno Plat Další Jméno Plat Další

Jednoduchý spojový seznam Jednoduchý = propojený 1 pointerem Spojový = realizovaný pomocí pointerů (spojů) Seznam = nějaká data Další nějaká data Další nějaká data Další 

Seznam Aby se k seznamu dalo přistupovat, potřebujeme jednu statickou proměnnou, která bude ukazovat na jeho začátek (obvykle se nazývá Hlava) Ostatní prvky seznamu už mohou být vytvořeny ve volné paměti (heapu)

Vytvoření seznamu Na začátku je seznam prázdný, tzn. neobsahuje žádný prvek, proto Hlava neukazuje nikam, má hodnotu NIL var Hlava: ^TPrvek; begin Hlava := Nil;

Přidat prvek na začátek seznamu Hlava Pom na začátku máme hlavu seznamu Hlava (obsahuje NIL) a pomocnou proměnnou Pom (typu ^TPrvek) X X

Přidat prvek na začátek (krok 1) Hlava Pom pomocí New vytvoříme nový prvek, na který ukazuje Pom do nového prvku naplníme data X Jméno Plat

Přidat prvek na začátek (krok 2) Hlava Pom to, co předtím bylo v pointeru Hlava, překopírujeme do položky Další toho nového prvku (v našem příkladu tam byla hodnota NIL, proto se do Pom^.Další vlozi NIL) Pom^.Další := Hlava; X Jméno Plat X

Přidat prvek na začátek (krok 3) Hlava Pom do pointeru Hlava nakopírujeme obsah Pom, čili adresu nového prvku tím je přidání skončeno poznámka: nemohli jsme provést rovnou New(Hlava), protože by se nám tím ztratil původní obsah pointeru Hlava Jméno Plat X

Přidat prvek na začátek (příklad) Hlava Pom var Pom: ^TPrvek; begin New(Pom); Pom^.Jmeno := …..; Pom^.Další := Hlava; Hlava := Pom; Jméno Plat X

Přidat prvek na začátek (další) postupovali jsme takto, protože to je univerzální algoritmus, který lze aplikovat i na částečně už zaplněný seznam Hlava Pom Jméno Jméno Plat Plat X

Procházení seznamem seznamem se dá procházet jen jedním směrem potřebujeme pomocný pointer Pom začíná se vždy v Hlava Pom := Hlava; procházení: Pom := Pom^.Další; Hlava Jméno Plat Jméno Jméno Plat Plat X

Seznam x zásobník Povšimněte si, že když procházíme seznam od Hlavy ke konci, pořadí prvků je od nejnovějšího po nejstarší, tedy LIFO tedy zásobník lze realizovat jako specielní případ seznamu, u kterého přidáváme na začátek a čteme od Hlava

Přidat prvek na konec seznamu na konec seznamu se přidává špatně, protože bychom museli nejprve ten konec najít (což je zdlouhavé) proto vedle pointeru Hlava zavedeme ještě další pointer ZaKonec, který bude ukazovat za konec seznamu na sentinel za koncem seznamu vytvoříme pomocný prvek, sentinel (nárazník)

Vytvoření seznamu ani nově vytvořený seznam tedy nebude prázdný, protože bude obsahovat sentinel begin New(Hlava); ZaKonec := Hlava; Hlava^.Další := Nil; Hlava ZaKonec (nic) (nic) X

Práce se seznamem Prohledávání tohoto seznamu i přidávání prvků na začátek je stejné

Vložení prvku na konec (krok 1) do sentinelu se naplní data Hlava ZaKonec ... Jméno Plat X

Vložení prvku na konec (krok 2) za původním sentinelem se vytvoří sentinel nový New(ZaKonec^.Dalsi); Hlava ZaKonec ... Jméno Plat (nic) (nic) (nic)

Vložení prvku na konec (krok 3) pointer ZaKonec se přesune na nový sentinel doplní se NIL v novém sentinelu ZaKonec := ZaKonec^.Dalsi; ZaKonec^.Další := Nil; Hlava ZaKonec ... Jméno Plat (nic) (nic) X

Seznam x fronta Povšimněte si, že když procházíme od Hlavy seznam se vkládáním „na konec“, pořadí prvků je od nejstaršího po nejnovější, tedy FIFO tedy frontu lze realizovat jako specielní případ seznamu, u kterého přidáváme na konec a čteme od Hlava

Vložení prvku doprostřed Pro vkládání „doprostřed“ i pro další operace potřebujeme nějak označit místo v seznamu, se kterým pracujeme proto zavedeme další statickou proměnnou Značka Značka bude vždy ukazovat na ten prvek seznamu, se kterým pracujeme

Vložení prvku za značku (krok 1) Pom Značka využijeme pomocnou proměnnou k vytvoření a naplnění nového prvku New(Pom); Pom^.Jmeno := …; Jméno Jméno Plat Plat

Vložení prvku za značku (krok 2) Pom Značka adresu následujícího prvku nakopírujeme do nově vytvořeného prvku Pom^.Další := Značka^.Další; Jméno Jméno Plat Plat

Vložení prvku za značku (krok 3) Pom Značka adresu nového prvku vložíme do „Další“ prvku, na který ukazuje značka Značka^.Další := Pom; Jméno Jméno Plat Plat

Odstranit prvek za značkou Podobně jako vložit, i odstranit prvek za značkou je snadné

Odstranit prvek za značkou(1) do pomocné proměnné Pom si zapamatujeme adresu rušeného prvku Pom := Značka^.Další Značka Pom Jméno toto Jméno Plat odstranit Plat

Odstranit prvek za značkou(2) změníme pointer na následující prvek seznamu Značka^.Další := Pom^.Další Značka Pom Jméno toto Jméno Plat odstranit Plat

Odstranit prvek za značkou(3) nepotřebný prvek zrušíme Dispose (Pom); Značka Pom Jméno Jméno Plat Plat

Odstranit prvek se značkou Přestože se zdánlivě jedná o podobný případ, je to mnohem obtížnější Důvod: nevíme, odkud vychází modrý pointer, proto ho nemůžeme změnit, jak bychom potřebovali (červená) Značka toto Jméno odstranit Plat

Možnosti řešení najít ten neznámý „předcházející“ prvek a tím to převést na řešení předchozí úlohy „odstranit prvek za značkou“ - pomalé ! nebo znát „fintu“ (viz dále) Poznámka: u zkoušky nutno buď znát fintu, nebo umět napsat program, jímž se najde „předcházející“ prvek

Finta (výchozí stav) toto je výchozí stav Značka toto Jméno odstranit Plat

Finta (krok 1) Značka Pom na prvek, následující za prvkem označeným značkou, si nastavit pomocnou proměnnou Pom := Značka^.Další; toto Jméno odstranit Plat

Finta (krok 2) Značka Pom do prvku, na kterém je značka, nakopírovat celý prvek za ním následující úloha se tím vlastně převedla na předcházející úlohu „odstranit prvek za značkou“, kterou už umíme řešit Značka^ := Pom^; Jméno Jméno Plat Plat

Finta (krok 3) pomocí Dispose zrušit prvek, na který ukazuje Pom Značka Pom X pomocí Dispose zrušit prvek, na který ukazuje Pom Dispose(Pom); Jméno Plat

Vložit prvek před značku Úloha: vložte nový prvek před prvek, na který ukazuje značka Problém: stejný jako při „odstranit prvek se značkou“ Řešení: podobné jako v předchozím. Vložit nový prvek za značku a pak do něj obsah prvku se značkou okopírovat. Nakonec vložit nová data tam, kam ukazuje Značka

Seznam je trojice Trojice pointerů Hlava + ZaKonec + Značka je pro seznam tak častá a charakteristická, že někteří autoři pod pojmem „seznam“ chápou právě tuto trojici

Seznam - realizace pomocí pole Byla ukázána realizace seznamu pomocí dynamických proměnných; to ale neznamená, že seznam nelze realizovat statickými proměnnými, např. pomocí pole: A H O J volné místo Značka ZaKonec

Uspořádaný seznam pro Seznam je typické, že prvky se identifikují pomocí jejich pozice v seznamu pro zvýšení rychlosti hledání můžeme seznam uspořádat, tzn. jeho prvky seřadit podle hodnot jejich klíčů, ale není to moc výhodné (spojové seznamy nelze snadno půlit, viz metoda půlení) struktura, ve které jsou prvky identifikovány hodnotou svého klíče, se nazývá tabulka

Dvojitý seznam Největší nevýhodou jednoduchého seznamu je, že neumíme rychle najít předcházející prvek (procházení je jednosměrné) kde to vadí, použijeme dvojitě zřetězený seznam: (nějaká data) (nějaká data) (nějaká data) Nahoru Nahoru Nahoru Dolů Dolů Dolů

Stromy pomocí recordů, ze kterých vede více pointerů, se snadno realizují stromy nejčastější strom je dichotomický, který má z každého uzlu 2 pointery: na levý podstrom a na pravý podstrom

Strom - terminologie (nějaká data) kořen podstrom (zde levý) Klíč uzly Pravý uzly větve (nějaká data) (nějaká data) Klíč Klíč listy Levý Pravý Levý Pravý

Binární strom Binární strom = dichotomický strom, pro který navíc platí, že všechny klíče v levém podstromu jsou menší a v pravém podstromu větší než klíč (předpoklad: klíče jsou jednoznačné)

Binární strom - příklady 32 45 26 kdyby zde bylo 36 nebyl by binární 14 63 28

Binární strom - význam nejdůležitější vlastnost: snadné a velmi rychlé hledání půlením je-li N počet uzlů, tak počet kroků  log2N

Binární strom: hledáme klíč=28 28<32, proto pokračuji levým podstromem 28>26, proto pokračuji praým podstromem 32 45 26 14 63 28 Klíč nalezen. Dojde-li se na list, aniž by byl klíč nalezen, pak neexistuje.

Binární strom: vložit klíč=27 27<32, proto pokračuji levým podstromem 27>26, proto pokračuji praým podstromem 32 45 26 14 63 28 Klíč neexistuje. Protože 27<32, vkládáme ho do levé větve 27

Hromada = heap jedná se o jiný heap než byla volná paměť! Heap je forma, jak binární strom zapsat do pole (a ušetřit tak místo na pointery) vychází z toho, že binární strom má v kořenu 1 uzel, v 1. úrovni 2 uzly, ve 3. úrovni 4 uzly atd, tedy v N-té úrovni je 2N uzlů

Heap - princip proto se v poli postupně pro i-tou úroveň přidělí 2N pozic (místa pro uzly, které ve stromu chybí, zůstanou prázdná) poměrně jednoduchým algoritmem se dá najít vztah mezi pozicí uzlu v binárním stromě a mezi jeho indexem v poli=heapu protože pozice všech uzlů jsou známé, lze pomocí heapu realizovat mnohé algoritmy, které pomocí pointerů napsat nelze

Heap - znázornění 1 2 3 4 5 6 7 8 9 10 11 12 13 ...

Heap - výpočet pozice nechť kořen je na indexu 1 jestliže jsme v uzlu s indexem K, tak (pokud existují): jeho rodič má index K div 2 levý podstrom má index 2*K pravý podstrom má index 2*K + 1

Nevýhoda binárního stromu při nevhodném pořadí vkládaných uzlů mohou větve narůstat nerovnoměrně, až v krajním případě může strom zdegenerovat na lineární seznam: 32 33 X 34 X 35 X X

AVL stromy odstraňují hlavní slabinu binárního stromu AVL strom je binární strom, u kterého navíc pro každý jeho uzel platí, že výška levého podstromu se od výšky pravého podstromu liší nejvýše o 1 abs( High(L) - High(P) )  1 čili Nevyváženost  1

Výška (pod)stromu 32 levý = 3 pravý = 2 45 26 14 63 28 27

Nevyváženost (pod)stromu 32 45 26 14 63 28 nevyváženost = 1 27

AVL strom - jak dosáhnout pomocí Nevyváženosti je definováno, co AVL strom je, ale nijak se tím neřeší, jak ho vytvořit platí: každý binární strom lze přetransformovat na AVL strom, a to pomocí konečného počtu rotací, aplikovaných na vhodné uzly stromu

Pravá rotace (výchozí stav) toto je kořen, strom není AVL, protože Nevyváženost = 2 32 45 26 14 28 27

Krok 1: z tohoto uzlu uděláme kořen Pravá rotace (krok 1) 32 Krok 1: z tohoto uzlu uděláme kořen Problém: na kořen nesmí vést žádná větev Poznámka: Kdyby se jednalo o rotaci podstromu, změníme pointer co vedl na uzel 32 tak, aby ukazoval na uzel 26. 45 26 14 28 27

směr této větve obrátíme, Pravá rotace (krok 2) 32 Krok 2: směr této větve obrátíme, aby byla dodržena „podmínka pro kořen“ Problém: z tohoto uzlu vedou 3 větve 45 26 14 28 27

Pravá rotace (krok 3) 32 Krok 3: tuto větev přesuneme pod vedlejší uzel 45 26 14 28 27

Pravá rotace (výsledek) 26 14 32 28 45 27

Rotace - poznámky předchozí příklad převedl binární strom na AVL nikde ale není psáno, že každá rotace (a nad každým uzlem) musí vést k výsledku! Nevhodná aplikace může situaci i zhoršit. podobně jako pravá rotace, existuje i levá rotace. Je zcela symetrická (zkouší se!)

Rotace - princip aplikace princip, podle kterého se aplikují rotace, vychází z faktu, že k AVL stromu lze uzel přidat v zásadě 6 způsoby: A B A levá rotace kolem uzlu A vyrovná nevyváženost na 0 přidané uzly pravá rotace kolem uzlu A a následně levá rotace kolem uzlu B vyrovná nevyváženost na 1

B-stromy pozor - „B“ neznamená binární, ale „Bayerovy“ doposud probrané struktury předpokládaly stejně rychlý přístup ke všem prvkům, což v praxi neplatí (paměť:disk až 106) a neomezenou velikost paměti (což také neplatí) B-stromy dávají dobrý návod, jak optimalizovat stránkování paměti (=přesouvání mezi pamětí a diskem) z hlediska rychlosti

B-stromy stránkování model stránkování (kdyby se do paměti vešly jen 5 uzlů):

B-stromy řádu K každá stránka obsahuje maximálně 2K a minimálně (s výjimkou kořene) K uzlů každá stránka je buď list (=nevedou z ní žádné větve) anebo z ní vede M+1 větví, kde M=aktuální počet uzlů ve stránce všechny listové stránky jsou na téže úrovni strom narůstá „vzhůru“, tzn. u kořene

B-strom řádu K=2, příklad stránky musejí mít 2..4 uzly: 25 10 20 30 40 <10 >20 mezi 2 5 7 8 13 14 15 18 22 24

B-strom, vložit 23 najít správnou stránku pokud je místo, vložit do stránky <25 25 10 20 30 40 >20 2 5 7 8 13 14 15 18 22 23 24

B-strom, vložit 17 najít správnou stránku protože už není místo, nutno přeuspořádat 10 20 10 15 20 17 13 14 17 18 13 14 15 18 13 14 15 17 18 vybrat prostřední a vytlačit nahoru

B-strom, vloženo 17, výsledek kdyby se i horní stránka přeplnila, pokračovalo by se analogicky vzhůru 25 10 15 20 30 40 2 5 7 8 13 14 17 18 22 23 24

B-stromy, strategie stránkování lze předpokládat, že bude vyžadován přístup k datům „blízko sebe“ v paměti držet vrcholovou stránku + z každé úrovně stránku na kterou se přistupovalo naposledy

Hledání Předpoklady: jednoznačný (=neopakující se) klíč, žádné dodatečné informace, žádná dodatečná paměť

Přímé (indexové) hledání v tabulce tabulka musí mít podobu pole klíč musí být malé ordinální číslo klíč se přímo použije jako index do pole; na daném indexu se nacházejí data nevýhody: index musí být ordinální a vede k řídkému poli 1 2 3 4 5 6 7 B A C D

Postupné prohledávání nejtriviálnější, ale pomalé N i := 0; while (i<Počet-1) and (Pole[i].Klíč <> HledanyKlic) do i := i+1; if Pole[i].Klíč=HledanyKlic then Nalezeno; 1 2 3 4 5 6 7 C B A D

Postupné prohledávání, nárazník cíl: odstranit zbytečný test na konec pole za konec pole vložit hledanou hodnotu klíče (nárazník=sentinel) tím je zaručeno, že se klíč vždycky najde po ukončení cyklu nutno testovat, zda bylo nalezeno uvnitř pole, nebo zda se jedná o sentinel

Nárazník = sentinel i := 0; Pole[Počet].Klíč := ‘K‘; while Pole[i].Klíč <> ‘K‘` do i := i+1; if i<Počet then Nalezeno; 1 2 3 4 5 6 7 C B A D K

Metoda půlení pole dodatečná podmínka: setříděnost tabulka musí mít podobu pole velká výhoda: rychlost log2N je možno použít na jakýkoliv klíč (i nečíselný, např. na stringy) velká nevýhoda: data se musejí udržovat uspořádaná, což zvyšuje režii vhodné: kde se málo vkládá a často čte

Metoda půlení (krok 1) máme 2 indexy: Dn=0 a Up=PočetKlíčů nalezneme Střed := (Dn+Up) div 2; Dn Střed Up 1 2 3 4 5 6 7 A B C D Q X Y Z

Metoda půlení (krok 2) předpokládejme, že hledáme klíč „C“ porovnáme klíč s obsahem prvku pod indexem Střed (je to „D“) protože klíč<Pole[Střed], vybereme dolní polovinu Up:=Střed-1 Dn Střed Up 1 2 3 4 5 6 7 A B C D Q X Y Z

Metoda půlení (krok 3) znovu najdeme Střed:=(Up+Dn) div 2=„B“ protože klíč<Pole[Střed], vybereme horní polovinu Dn:=Střed+1 postup se opakuje, dokud buď není nalezen klíč, nebo se nenajde konec Up<Dn Střed Dn Up 1 2 3 4 5 6 7 A B C D Q X Y Z

Metoda transformace klíče = metoda hashování [hešování] vychází z přímého (indexového) hledání odstraňuje jeho nedostatky (řídké pole a nutnost ordinálního indexu) libovolný typ indexu (např. string) velmi rychlý přístup

Transformace klíče, příklad klíčem nechť je plat metodu vysvětlíme na příkladu podle vedlejší tabulky zvolím hashovací funkci f(X):=X mod 10 pozor, to není dobrá volba (zde jsem ji použil jen pro její názornost) Mozart 2530 Brahms 1893 Beethoven 5422 Chopin 2215 Bach 6048 Čajkovskij 4914 Verdi 3175

Transformace klíče, vkládání údaje budeme vkládat do pole A délky 10 vkládáme tak, že klíč nejprve přetransformujeme hashovací funkcí a pak číslo, které dostaneme, použijeme jako index do pole A stejně se postupuje pro všechny údaje 1 2 3 4 5 6 … Chopin: 2215 h(2215) = 5 vložit sem

Transformace klíče, kolize v okamžiku, kdy se pokusíme vložit Verdi: 3175 nastane problém: pozice už je obsazena ! nastala kolize existují 2 způsoby řešení kolize: vnitřní vnější 0 Mozart 1 2 Beethoven 3 Brahms 4 Čajkovskij 5 Chopin 6 7 8 Bach 9 Verdi: 3175 h(3175) = 5 KOLIZE

Vnější řešení kolize vnější, protože se řeší vně hashovacího pole 4 princip: pozice nalezená hashováním je současně hlavou spojového seznamu (kolizní seznam) 4 5 Chopin: 2215 6 Verdi: 3175

Vnitřní řešení kolize (1) údaje se ukládají uvnitř hashovacího pole je třeba upravit algoritmus ukládání i hledání ! když nastane kolize, hledá se první volné místo a na to se údaj uloží

Vnitřní řešení kolize (2) modifikace: vkládání na nejbližší volné místo (index+1) vede k shlukům proto se někdy krok volí proměnlivý podle nějaké tabulky hledání tomu musí odpovídat 0 Mozart: 2530 1 2 Beethoven: 5422 3 Brahms: 1893 4 Čajkovskij:4914 5 Chopin: 2215 6 Verdi: 3175 7 8 Bach: 6048 9 Verdi: 3175 h(3175) = 5 Kolize index 5 je obsazen, proto vložíme na nejbližší volný (6)

Vnitřní řešení kolize (3) jiný příklad: někdy se musí dát dost daleko Hledání: hledá se buď klíč, nebo první volné místo ! 0 Mozart: 2530 1 2 Beethoven: 5422 3 Brahms: 1893 4 Čajkovskij:4914 5 Chopin: 2215 6 Verdi: 3175 7 Dvořák: 2723 8 Bach: 6048 9 Dvořák: 2723 h(2723) = 3 Kolize index 3 je obsazen, proto vložíme na nejbližší volný ( až 7! )

Vnitřní řešení kolize (4) Problém: při vnitřním řešení kolize není možno údaje vymazat! kdybychom např. vymazali Chopin:2215, vzniklo by volné místo, které by znemožnilo nalezení Dvořák i Verdi proto se „vymazané“ údaje jen označí jako neplatné a při nejbližší příležitosti přepíší 0 Mozart: 2530 1 2 Beethoven: 5422 3 Brahms: 1893 4 Čajkovskij:4914 5 Chopin: 2215 6 Verdi: 3175 7 Dvořák: 2723 8 Bach: 6048 9

Volba hashovací funkce cílem je zvolit funkci, která údaje rozprostře rovnoměrně z typu klíče udělá ordinální číslo omezené velikosti (odpovídající velikosti hashovacího pole) z tohoto hlediska DIV ani MOD 10 nejsou dobré dobré jsou např. MOD Prvočíslo, MD5, … stringy a recordy možno brát jako binární

Třídění = řazení Předpoklady: jednoznačný (=neopakující se) klíč, žádné dodatečné informace, žádná dodatečná paměť

Bublinkové třídění Bubblesort nejtriviálnější, ale nejméně účinné N2 postupné přesouvání dat připomíná „probublávání“ bublin v kapalině Postupně se testují dvojice za sebou jdoucích dat: Pokud první údaj je větší než druhý, navzájem se přehodí. Přejde se na další dvojici

Bubblesort (příklad)... 15 68 17 84 21 18 33 96 1. Klíče jsou ve správném pořadí (15<68), žádná akce 2. Nesprávné pořadí, prohodit 68 a 17 3. Klíče jsou ve správném pořadí (teď se testuje 68 proti 84), žádná akce …. a tak dále až do konce dat

… pokračování 15 68 17 84 21 18 33 96 15 17 68 84 21 18 33 96 15 17 68 21 84 18 33 96 15 17 68 21 18 84 33 96 15 17 68 21 18 33 84 96 15 17 68 21 18 33 84 96 nový průchod atd.

Bubblesort - počet průchodů na efektivnost bubblesortu má vliv, kolikrát se polem dat prochází nejjednodušší podmínka: je-li dat N, pole se projde N-krát. Protože při každém průchodu se hodnota posune aspoň o 1 pozici, jsou po N průchodech i v nejnepříznivějším případě všechna data na svém místě

Bubblesort II podmínku N průchodů lze modifikovat: zavede se pomocná proměnná, která se na začátku každého průchodu nastaví na False kdykoliv dojde k prohození dat, pomocná proměnná se nastaví na True pokud je po skončení průchodu pomocná proměnná False, nedošlo k žádnému prohození, tudíž data jsou setříděna a je možno skončit

Bubblesort III efektivnost lze ještě dále zvýšit tím, že se nebudou procházet všechna data: při každém průchodu si poznamenáme pozici P1 první změny a P2 poslední změny následující průchod se provádí jen od P1-1 do P2+1

Shakesort nepříjemnou vlastností bubblesortu je nesymetrie: „84“ se na správné místo dostala už během prvního průchodu, zatímco „18“ postoupila jen o 1 pozici řešení: pole střídavě procházet shora a zdola programová realizace: dva cykly (to/downto) za sebou 15 17 68 84 21 18 33 96 15 17 68 21 84 18 33 96 15 17 68 21 18 84 33 96 15 17 68 21 18 33 84 96

Shellsort 15 17 68 84 21 18 33 96 15 17 68 84 21 18 33 96 15 17 21 18 68 84 33 96 pokusy o urychlení: nejprve dlouhý krok, aby se data rychle dostala do blízkosti správné posice, pak postupně zjemňovat existují doporučené posloupnosti kroků

Insertsort další triviální, ale pomalý algoritmus N2 funguje, jako když si člověk rovná v ruce karty: nalevo už jsou setříděné, napravo ještě nesetříděné z nesetříděných se postupně zleva doprava jedna vybere… …a zasune na správné místo mezi setříděné (mezera se vytvoří posunutím karet)

Insertsort příklad index= 1 2 3 4 5 6 vezmeme číslo a postupně ho od začátku porovnáváme se setříděnými, až nalezneme správnou pozici vytvoříme mezeru tak, že setříděné (od té mezery) posuneme o 1 pozici doleva číslo do vzniklé mezery vložíme a tím je zatříděno 5 2 4 6 1 3 2 5 4 6 1 3 2 4 5 6 1 3 2 4 5 6 1 3

Insertsort program for j:=Low(A)+1 to High(A) do begin Klic := A[j]; i := j-1; while (i>0)and(A[i]>Klic) do A[i+1] := A[i]; i := i-1; end; A[i+1] := Klic; index= 1 2 3 4 5 6 5 2 4 6 1 3 2 5 4 6 1 3 2 4 5 6 1 3 2 4 5 6 1 3

Quicksort vynikající rychlost řádu N log2N, což je mezi tříděními bez dodatečných předpokladů nejlepší známý výsledek kombinuje rekurzi s metodou půlení

Quicksort krok 1 zvolíme medián. (Medián je prvek s prostřední hodnotou.) Protože nalezení mediánu je časově náročné, nahrazuje se nalezením prvku ležícího uprostřed (zhoršení rychlosti je obvykle malé) obvykle se realizuje pomocí div 2 5 2 4 6 1 3

Quicksort krok 2 postupujeme zleva doprava a najdeme první prvek větší než medián postupujeme zprava doleva a nalezneme první prvek menší než medián oba prvky spolu prohodíme 5 2 4 6 1 3

Quicksort krok 2 dokončení kdyby se (jako nám) stalo, že na jedné straně už nebude s čím prohazovat, prohodíme s mediánem skončíme, když všechny prvky menší než medián jsou nalevo a všechny prvky větší napravo od mediánu 3 2 4 6 1 5 3 2 1 6 4 5 3 2 1 6 4 5 3 2 1 4 6 5

Quicksort krok 3 skončili jsme ve stavu, kdy nalevo od mediánu jsou všechny prvky menší než medián (byť nesetříděné) a napravo všechny prvky větší než medián na obě tyto části rekurzívně aplikujeme stejný algoritmus 3 2 1 4 6 5 rekurze rekurze

Třídění v lineárním čase z toho, že Quicksort je nejrychlejší algoritmus pracující bez dodatečných předpokladů nelze usuzovat, že je za všech okolností nejlepší ukážeme 2 algoritmy, které s poměrně malými předpoklady dosahují dokonce lineární rychlosti, tj. rychlost N

Počítací třídění Counting sort předpoklad: pro nějaké (ne moc velké) K všechny klíče jsou typu integer  0..K základní myšlenka: když pro každý prvek X víme, kolik prvků je menších, můžeme X rovnou zatřídit příklad: jestliže víme, že 17 prvků je menších než X, tak X musí ležet na 18. pozici s malou modifikací lze zpracovat i případ více shodných klíčů

Counting sort princip (1) Budeme pracovat se 3 poli: A[1..N] bude obsahovat vstupní data, B[1..N] setříděná data a C[1..K] je pomocné. N je počet dat a K je limit velikosti klíče s pomocí této znalosti přeřadíme prvky z B do A A 2 5 3 0 2 3 0 3 B ? ? ? ? ? ? ? ? C 0 0 0 0 0 0

Counting sort princip (2) A 2 5 3 0 2 3 0 3 do C[i] napočítáme počet prvků s klíčem rovným i hodnoty sečteme tak, aby C[i] obsahovalo počet prvků s klíčem menším než i 0 1 2 3 4 5 C 2 0 2 3 0 1 A 2 5 3 0 2 3 0 3 0 1 2 3 4 5 C 2 2 4 7 7 8

Counting sort princip (3) postupně pro všechny prvky z A, zprava doleva: vezmeme prvek jeho klíč použijeme jako index do C hodnotu nalezenou v C použijeme jako index do B, kam uložíme prvek A 2 5 3 0 2 3 0 3 3 0 1 2 3 4 5 C 2 2 4 7 7 8 7 1 2 3 4 5 6 7 8 B ? ? ? ? ? ? 3 ?

Counting sort princip (4) předcházející postup by vyhovoval, kdyby všechny klíče byly jedinečné připouštíme-li opakování klíčů, po každém zápisu do B musíme zmenšit C[i] aby následující zápis šel na předchozí index takže v našem příkladu se C[i] zmenší ze 7 na 6 a následující klíč 3 už půjde do B[6] výsledné pole B po zpracování: 1 2 3 4 5 6 7 8 B 0 0 2 2 3 3 3 5

Counting sort program for i := 0 to K do C[i] := 0; for i := 1 to High(A) do C[ A[i] ] := C[ A[i] ] + 1; for i := 1 to K do C[i] := C[i] + C[i+1]; for i := High(A) downto 1 do begin B[ C[A[i]] ] := A[i]; C[ A[i] ] := C[ A[i] ] - 1; // dec(….) end;

Bucket sort bucket = kbelík dává dobrou rychlost (skoro lineární N) za předpokladu rovnoměrného rozložení, tzn. že všechny klíče jsou stejně pravděpodobné funguje na klíče real  <0..1) nebo když lze klíče na tento interval přetransformovat

Bucket sort princip interval <0..1) se rozdělí na N stejných podintervalů (to jsou ty kýble ) protože rozdělení je rovnoměrné, do každého intervalu padne jen málo klíčů, takže setřídění uvnitř intervalu je snadné a rychlé (trochu to připomíná hashování) postupně projdeme intervaly a v nich klíče podle velikosti

Bucket sort příklad for i := 1 to High(A) do begin Podle A[i] určit interval j; Přidat A[i] k seznamu začínajícímu v B[j]; end; for j := 0 to 4 do Setřídit seznam B[j]; Spojit seznamy Vstup (pole A): 1 2 3 4 5 0,78 0,17 0,39 0,26 0,72 Pracovní pole B: 0 0,2 0,4 0,6 0,8 1 0 1 2 3 4 | | | | 0,17 0,39 0,78 0,26 0,72

The End