Seminář z IVT Algoritmizace Slovanské gymnázium Olomouc Tomáš Kühr
Algoritmizace - o čem to je? Zatím jsme se zabývali především tím, jak určitý postup zapsat v konkrétním programovacím jazyce (např. C#) Neméně důležité je ale tento postup řešení vymyslet Případně vymyslet nejlepší možný postup nejrychlejší nejméně náročný na paměť dávající nejpřesnější výsledek zaručeně správný nebo jakoukoli vhodnou kombinaci výše uvedeného 2
Algoritmus V širším smyslu se jedná o návod nebo postup. V užším smyslu: Algoritmus je posloupnost instrukcí pro řešení daného problému. Tato definice je však značně nepřesná: Co je instrukce? Co je problém? Co je jeho řešení? Existuje i matematicky přesná definice, ale tím se nebudeme zabývat 3
Algoritmus - upřesnění Běžně nám ale stačí následující upřesnění: Instrukce je jednoznačný a srozumitelný pokyn, který může člověk nebo počítač mechanicky (tj. bez dalšího přemýšlení) vykonávat Problém je zadám otázkou/úkolem a množinou možných zadání (vstupů) (např. Je číslo na vstupu prvočíslo? + množina všech přirozených čísel) Řešení problému algoritmem spočívá v postupném provádění instrukci Jako řešení problému se také označuje výstup tohoto procesu (pokud nějaký výsledek existuje) Takto specifikovaný pojem algoritmu odpovídá počítačovým programům 4
Příklady problémů Slovo problém používáme i v jiných situacích: Problém zaparkování auta (Odpovídá naší definici; vstup: poloha auta a překážek v okolí; výstup: není; instrukce: otoč volantem o X stupňů, sešlápni plynový pedál, ) Problém vyřešení kvadratické rovnice ax 2 + bx + c (Odpovídá definici; vstup: 3 reálná čísla a, b, c; výstup: reálná čísla x1 a x2; aritmetické instrukce, přiřazení) Problém přípravy svíčkové s knedlíkem (Neodpovídá naší definici - není jasné, co je dostatečně dobrá svíčková; také recepty většinou nejsou dostatečně jednoznačné - můžeme jídlo pokazit) Izraelsko-Palestinský problém (neodpovídá definici) Problém, jak žít smysluplný život (neodpovídá definici) 5
Příklad algoritmu Řešení kvadratické rovnice Vstup: reálná čísla a, b, c Výstup: reálná čísla x 1, x 2 (nebo informace, že nemá řešení) Instrukce: 1. Vypočítej diskriminant D pomocí vzorce D = b 2 4ac. 2. Pokud je D kladné, pak vypočítej kořen x 1 = b D 2a a kořen x 2 = 3. Pokud je D nula, pak existuje dvojnásobný kořen x 1 = x 2 = b 2a. 4. Pokud je D záporné, pak rovnice nemá reálné kořeny. b+ D 2a. 5. Vrať kořeny x 1 a x 2. 6
Základní vlastnosti algoritmů Jednoduchost (elementárnost) = Algoritmus se skládá z jednoduchých (elementárních) kroků. Jednoznačnost (determinovanost) = Kroky algoritmu jsou zcela jednoznačné a po každém kroku je jasně dáno, kterým krokem bude algoritmus pokračovat. Konečnost (finitnost) = Algoritmus vždy skončí po konečném počtu kroků. Obecnost (hromadnost, univerzálnost) = Algoritmus neřeší jeden konkrétní problém, ale obecnější třídu problémů. (Vstupy algoritmu je možné měnit.) Výstup (resultativnost) = Algoritmus má nějaký výsledek. Výsledkem může být i informace, že rovnice nemá řešení (viz předchozí příklad)- 7
Možnosti popisu algoritmů Existuje velké množství možností, jak algoritmus popsat Některé již známe: Slovní popis v přirozeném jazyce (např. češtině) Výhody: snadno srozumitelné laikům Nevýhody: zdlouhavé, často nejednoznačné a méně přehledné Popis v nějakém programovacím jazyce (např. C#) Výhody: naprosto přesné, lze přímo přeložit a program používat Nevýhody: dlouhé, obsahuje nepodstatné detaily Další (pro nás zatím neznámé) možnosti:... Pseudokód (viz dále) Vývojové diagramy 8
Pseudokód Programovacím jazykům blízký způsob zápisu algoritmů Neřeší ale spousty nepodstatných detailů Výhody: Stručný, úsporný způsob zápisu Přehledný a srozumitelný i neprogramátorům (matematikům, fyzikům,...) Lze snadno převést do téměř libovolného programovacího jazyka Nevýhody: Horší čitelnost pro netechnické profese Je vždy potřeba přepsat do konkrétního programovacího jazyka 9
Instrukce pseudokódu V instrukcích pseudokódu se typicky vyskytují Proměnné (budu psát kurzívou) Operátory (+,, *, /, mod, and, or, not,, =, <, >,,,,, [ ], ) Větvení (if... then... else...) Cykly (while... do..., for... to... do... ) Příklad pseudokódu (algoritmus nalezení největšího společného dělitele): Vypocet-NSD(cislo1, cislo2) 1 while cislo2>0 2 do zbytek cislo1 mod cislo2 3 cislo1 cislo2 4 cislo2 zbytek 5 return cislo1 10
C# vs. pseudokód static int[] delitele(int cislo) { bool[] delitelnost = new bool[cislo + 1]; int[] vysledek; int pocetdelitelu = 0; for (int delitel = 1; delitel <= cislo; delitel++) { if (cislo % delitel == 0){ delitelnost[delitel] = true; pocetdelitelu++; } else { delitelnost[delitel] = false; } } vysledek = new int[pocetdelitelu]; for (int i = 1, i2 = 0; i<=cislo; i++) { if (delitelnost[i]) { vysledek[i2] = i; i2++ } } return vysledek; } delitele(cislo) 1 for delitel 1 to cislo 2 do if cislo mod delitel = 0 3 then 4 delitelnost[delitel] PRAVDA 5 pocetdelitelu pocetdelitelu+1 6 else 7 delitelnost[delitel] NEPRAVDA 8 i2 0 9 for i 1 to cislo 10 do if delitelnost[i] 11 then 12 vysledek[i2] i 13 i2 i2+1 14 return vysledek; 11
Sčítání dvou celých čísel slovní popis Vstup: a n-1 a n-2...a 1 a 0, b n-1 b n-2...b 1 b 0, kde a i a b i jsou čísla z {0, 1,, 9} Výstup: c n c n-1...c 1 c 0, kde c i jsou také čísla z {0, 1,, 9} a platí, že součet čísel daných číslicemi na vstupu ai a bi odpovídá číslu, které je dáno číslicemi c i Kroky algoritmu: 1. Je-li a 0 +b 0 < 10, nastav hodnotu c 0 na a 0 +b 0 a t na 0, jinak nastav c 0 na a 0 +b 0 10 a t na 1. 2. Je-li a 1 +b 1 +t < 10, nastav hodnotu c 1 na a 1 +b 1 +t a t na 0, jinak nastav c 1 na a 1 +b 1 +t 10 a t na 1. 3.... Je-li a n-1 +b n-1 +t < 10, nastav hodnotu c n-1 na a n-1 +b n-1 +t a t na 0, jinak nastav c n-1 na a n-1 +b n-1 +t 10 a t na 1. Nastav c n na t. 12
Sčítání dvou celých čísel lepší slovní popis Vstup: pole číslic a[n] a b[n] odpovídající vstupním číslům čteným od konce Výstup: pole číslic c[n+1] odpovídající výsledku čtenému od konce Kroky algoritmu: 1. Nastav t na 0. 2. Pro hodnoty i od 0 do n 1 prováděj postupně: Je-li a[i] + b[i] + t < 10, nastav c[i] na a[i] + b[i] + t a t na 0, jinak nastav c[i] na a[i] + b[i] + t 10 a t na 1; 3. Nastav c[n] na t. 13
Sčítání dvou celých čísel pseudokód Vstup: pole číslic a[n] a b[n] odpovídající vstupním číslům čteným od konce Výstup: pole číslic c[n+1] odpovídající výsledku čtenému od konce Pseudokód algoritmu: Scitani-Cisel(a[0..n 1], b[0..n 1]) 1 t 0 2 for i 0 to n 1 3 do c[i] (a[i] + b[i] + t) mod 10 4 t (a[i] + b[i] + t)/10 5 c[n] t 6 return c 14
Sčítání dvou celých čísel C# Vstup: pole číslic a[n] a b[n] odpovídající vstupním číslům čteným od konce Výstup: pole číslic c[n+1] odpovídající výsledku čtenému od konce Kód funkce: static int[] ScitaniCisel(int[] a, int[] b) { int t = 0; int n = a.length; int[] c = new int[n + 1]; for (int i = 0; i < n; i++) { c[i] = (a[i]+b[i]+t) % 10; t = (a[i] + b[i] + t) / 10; } c[n] = t; return c; } 15
Správnost algoritmu Správnost algoritmu není obvykle snadné ověřit Často se spokojíme s testováním algoritmu pro rozumně zvolené množství rozumně zvolených vstupů (běžné vstupní hodnoty, hraniční hodnoty i zakázané vstupy) Matematický důkaz správnosti algoritmů Většinou velmi náročný Používá se pro kritické systémy (letadla, jaderné elektrárny,...) Správnost algoritmu/programu nelze dokázat algoritmicky (tj. pomocí softwaru) 16
Složitost algoritmu Vlastnost algoritmů určující množství zdrojů potřebných pro jejich běh časová složitost (počet operací, využitý procesorový čas) paměťová složitost (velikost potřebné paměti) Podle složitosti můžeme algoritmy porovnávat Časová složitost se uvádí ve tvaru funkce, která velikosti vstupu přiřadí počet operací provedených algoritmem Při práci se složitostmi algoritmů obvykle zanedbáváme méně podstatné rozdíly (bereme jen nejrychleji rostoucí výraz a ignorujeme i jeho konstantní násobky) Logaritmická složitost log n 3log 5 (n+5), log 4 (n 2-5n+10), log 10 5n + log 3 (6n-2) Lineární složitost n 5n, 3n-2,10n+6, 5n+log 10 5n Linearithmetic complexity n log n 2n log 10 5n, 3(n-5) log 4 (n 2-5n+10),10n log 5 n + 12n - 6log 10 5n Kvadratická složitost n 2 6n 2 +5n-log 17 7 2n, (n-2)(n+5), 12n 2-5n+10 Exponenciální složitost 2 n, 3 n2-3n+4, 10 n +n 2-5
Ukázka analýzy algoritmu Vstup: pole číslic a[n] a b[n] Výstup: pole číslic c[n+1] Pseudokód algoritmu: Scitani-Cisel(a[0..n 1], b[0..n 1]) 1 t 0 2 for i 0 to n 1 3 do c[i] (a[i] + b[i] + t) mod 10 4 t (a[i] + b[i] + t)/10 5 c[n] t 6 return c Na vstupu jsou čísla s n ciframi, můžeme tedy použít n jako velikost vstupu (podobného výsledku dosáhneme, i pokud uvažujeme velikost vstupu jako 2n) Na řádku 1 je vždy 1 operace, cyklus na řádku 2 proběhne n-krát a obsahuje přiřazení do i a řádky 3 (4 operace) a 4 (5 operací), na řádku 5 máme 1 operaci a return na řádku 6 může být také chápán jako operace. Celkově máme složitost algoritmu 10n+3 18
Cvičení - návrh a analýza algoritmu Pro následující problémy: a) Vyhledání maximálního čísla v posloupnosti čísel b) Určení, zda je číslo prvočíslem c) Rozklad čísla na součin prvočísel d) Násobení 2 libovolně velkých čísel Popište problém co nejpřesněji, včetně formátu vstupů a výstupů Zapište algoritmus jejich řešení slovně Zapište algoritmus jejich řešení pomocí pseudokódu Pokuste se určit/odhadnout časovou složitost algoritmů 19