Algoritmus je presný postup, ktorý v konečnom počte krokov vyrieši úlohu. V tejto téme prejdeme klasické algoritmy, ktoré by mal poznať každý programátor: hľadanie najmenšieho a najväčšieho prvku, vyhľadávanie, triedenie a základné číselné algoritmy. Python má na mnohé z nich hotové funkcie (max, sort, in) – no pochopiť, ako fungujú, je dôležité: rovnaké myšlienky použiješ v zložitejších úlohách, kde hotová funkcia nestačí.
Čo sa naučíš
- nájsť minimum a maximum (aj ich pozíciu) bez vstavaných funkcií,
- vyhľadávať prvok lineárne aj binárne a vedieť, kedy ktoré použiť,
- triediť bublinkovým triedením a triedením výberom,
- programovať číselné algoritmy: NSD, prvočísla, delitele, prevody sústav.
1Čo je algoritmus
Algoritmus musí byť:
- jednoznačný – v každom kroku je jasné, čo sa má urobiť,
- konečný – po konečnom počte krokov skončí,
- všeobecný – rieši celú skupinu úloh (napríklad súčet ľubovoľných čísel, nie len 3 + 5),
- rezultatívny – dáva výsledok.
Algoritmus sa dá zapísať slovami, pseudokódom (zjednodušeným zápisom podobným programu) alebo vývojovým diagramom. Program je algoritmus zapísaný v programovacom jazyku.
Pri porovnávaní algoritmov nás zaujíma aj efektívnosť – koľko krokov potrebujú pre veľké vstupy. Pre zoznam s miliónom prvkov je obrovský rozdiel, či algoritmus potrebuje 20, milión alebo bilión krokov.
2Minimum a maximum
Myšlienka: prvý prvok vyhlásime za doteraz najväčší. Potom prechádzame ostatné prvky; ak je niektorý väčší, stane sa novým maximom.
Výstup:
12
-32.1Pozícia maxima
Často nás zaujíma, kde je maximum. Pamätáme si index namiesto hodnoty:
Výstup:
maximum 12 na indexe 2Pri rovnakých hodnotách nájde > prvý výskyt maxima; s >= by našiel posledný.
2.2Všetky výskyty minima
Výstup:
minimum: 1 počet: 3 pozície: [1, 3, 5]3Lineárne vyhľadávanie
Úloha: zistiť, či (a kde) sa hodnota nachádza v zozname. Lineárne vyhľadávanie prechádza prvky jeden po druhom, kým hodnotu nenájde. Ak prejde celý zoznam bez úspechu, hodnota v ňom nie je – zvykne sa vrátiť -1.
Výstup:
2
-1return i ukončí funkciu pri prvom nájdení. Až po cykle (keď sme prešli všetko) vraciame -1.
V najhoršom prípade (hodnota je na konci alebo chýba) treba porovnať všetkých n prvkov. Pre zoznam s miliónom prvkov je to milión porovnaní. Vstavané hodnota in z a z.index(hodnota) robia to isté – iba rýchlejšie, lebo sú naprogramované v jazyku C.
4Triedenie
Triedenie usporiada prvky podľa veľkosti. V praxi použiješ z.sort() alebo sorted(z), ale klasické algoritmy triedenia sú výborným tréningom práce s indexmi.
4.1Bublinkové triedenie
Myšlienka: prechádzame zoznam a porovnávame susedné dvojice. Ak sú v zlom poradí, vymeníme ich. Po prvom prechode „vybuble“ najväčší prvok na koniec, po druhom druhý najväčší na predposledné miesto atď.
Výstup:
po 1. prechode: [1, 4, 2, 5, 8]Prvý prechod podrobne:
| porovnanie | stav pred | výmena? | stav po |
|---|---|---|---|
| 5 a 1 | [5, 1, 4, 2, 8] | áno | [1, 5, 4, 2, 8] |
| 5 a 4 | [1, 5, 4, 2, 8] | áno | [1, 4, 5, 2, 8] |
| 5 a 2 | [1, 4, 5, 2, 8] | áno | [1, 4, 2, 5, 8] |
| 5 a 8 | [1, 4, 2, 5, 8] | nie | [1, 4, 2, 5, 8] |
Celé triedenie opakuje prechody. Po každom prechode je na konci o jeden zoradený prvok viac, takže ďalší prechod môže byť o jedno porovnanie kratší. Ak počas prechodu nenastala žiadna výmena, zoznam je zoradený a môžeme skončiť:
Výstup:
[1, 2, 3, 4, 5, 8]Funkcia nič nevracia – zoraďuje priamo zoznam, ktorý dostala (zoznam je meniteľný).
4.2Triedenie výberom
Myšlienka: nájdeme najmenší prvok a vymeníme ho s prvým. Potom nájdeme najmenší zo zvyšku a vymeníme ho s druhým atď.
Výstup:
[10, 13, 14, 29, 37]4.3Ako rýchle je triedenie
Bublinkové triedenie aj triedenie výberom porovnávajú v najhoršom prípade približne n²/2 dvojíc. Pre 1 000 prvkov je to asi 500 000 porovnaní, pre milión prvkov 500 miliárd – to by trvalo veľmi dlho. Vstavané sort používa šikovnejší algoritmus, ktorý potrebuje rádovo n · log₂ n porovnaní (pre milión prvkov asi 20 miliónov) – preto ho v skutočných programoch používaj.
>>> mena = ["Sofia", "ema", "Ján", "Adam"] >>> sorted(mena) ['Adam', 'Ján', 'Sofia', 'ema'] >>> sorted(mena, key=str.lower) ['Adam', 'ema', 'Ján', 'Sofia'] >>> sorted(mena, key=len) ['ema', 'Ján', 'Adam', 'Sofia']
Parameter key určuje, podľa čoho sa triedi: str.lower triedi bez ohľadu na veľké písmená, len podľa dĺžky.
5Binárne vyhľadávanie
Ak je zoznam zoradený, dá sa hľadať oveľa rýchlejšie. Poznáš to z hry „Myslím si číslo od 1 do 100“: najlepšie je tipnúť si 50 a podľa odpovede „viac/menej“ zahodiť polovicu možností.
Myšlienka: pozrieme sa na stredný prvok. Ak je to hľadaná hodnota, máme ju. Ak je hľadaná hodnota menšia, môže byť iba v ľavej polovici, inak iba v pravej. Hľadanie opakujeme v polovici, ktorá ostala.
Výstup:
5
-1Priebeh hľadania hodnoty 23:
| krok | lavy | pravy | stred | z[stred] | rozhodnutie |
|---|---|---|---|---|---|
| 1 | 0 | 9 | 4 | 16 | 16 < 23 → hľadáme vpravo |
| 2 | 5 | 9 | 7 | 56 | 56 > 23 → hľadáme vľavo |
| 3 | 5 | 6 | 5 | 23 | nájdené na indexe 5 |
5.1Prečo je binárne vyhľadávanie také rýchle
Každý krok zmenší oblasť hľadania na polovicu. Pre n prvkov treba najviac približne log₂ n krokov:
| počet prvkov | lineárne (najhorší prípad) | binárne (najviac) |
|---|---|---|
| 10 | 10 | 4 |
| 1 000 | 1 000 | 10 |
| 1 000 000 | 1 000 000 | 20 |
6Číselné algoritmy
6.1Najväčší spoločný deliteľ (Euklidov algoritmus)
Najväčší spoločný deliteľ (NSD) dvoch čísel sa dá hľadať skúšaním všetkých deliteľov, ale Euklidov algoritmus je oveľa rýchlejší. Využíva, že NSD(a, b) = NSD(b, a % b) a NSD(a, 0) = a:
Výstup:
6 12a | b | a % b |
|---|---|---|
| 48 | 18 | 12 |
| 18 | 12 | 6 |
| 12 | 6 | 0 |
| 6 | 0 | výsledok je 6 |
Najmenší spoločný násobok (NSN) dostaneme zo vzťahu NSN(a, b) = a · b / NSD(a, b). Python má na NSD aj hotovú funkciu math.gcd.
6.2Prvočísla
Číslo n > 1 je prvočíslo, ak nemá iného delitela ako 1 a n. Netreba skúšať všetky delitele do n: ak má n delitela väčšieho ako √n, musí mať aj delitela menšieho ako √n. Stačí preto skúšať delitele d, pre ktoré platí d * d <= n:
Výstup:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
TruePre číslo okolo milióna stačí vyskúšať asi 1 000 deliteľov namiesto milióna. (Podčiarkovníky v 1_000_003 iba sprehľadňujú zápis čísla.)
Eratostenovo sito nájde všetky prvočísla do n naraz: vypíšeme čísla 2 až n a postupne preškrtávame násobky každého ešte nepreškrtnutého čísla.
Výstup:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]6.3Delitele a dokonalé čísla
Výstup:
[1, 2, 4, 7, 14, 28]
6 je dokonalé číslo
28 je dokonalé číslo
496 je dokonalé čísloDokonalé číslo sa rovná súčtu svojich deliteľov menších ako ono samo: 28 = 1 + 2 + 4 + 7 + 14. Ďalšie dokonalé číslo je až 8128.
6.4Prevody medzi číselnými sústavami
Z desiatkovej do dvojkovej sústavy opakovane delíme dvoma a zvyšky čítame odzadu (téma Cykly). Opačne: každá cifra zľava vynásobí doterajší výsledok dvoma a pripočíta sa:
Výstup:
13 255
13 0b1101 0xffVstavané funkcie: int(text, základ) prevedie z ľubovoľnej sústavy, bin a hex do dvojkovej a šestnástkovej.
7Riešené úlohy
7.1Najdlhšia rastúca súvislá časť
Zadanie: V zozname nájdi dĺžku najdlhšej súvislej časti, v ktorej každé číslo je väčšie ako predchádzajúce.
Riešenie: Prechádzame zoznam a počítame dĺžku aktuálnej rastúcej časti. Keď rast skončí, začíname počítať od 1. Popri tom si pamätáme maximum – je to vlastne hľadanie maxima „za behu“.
Výstup:
57.2Zlúčenie dvoch zoradených zoznamov
Zadanie: Dva zoradené zoznamy spoj do jedného zoradeného bez použitia sort.
Riešenie: Dva „ukazovatele“ i a j ukazujú na najmenší ešte nepoužitý prvok každého zoznamu. Menší z nich pridáme do výsledku a posunieme príslušný ukazovateľ. Keď sa jeden zoznam minie, pridáme zvyšok druhého.
Výstup:
[1, 2, 3, 4, 9, 10, 12, 15, 20]Toto zlučovanie je základom rýchleho triedenia zlučovaním (merge sort).
Zhrnutie
- Algoritmus je jednoznačný, konečný, všeobecný a dáva výsledok; zaujíma nás aj jeho efektívnosť.
- Maximum: začni prvým prvkom (nie nulou), pamätaj si hodnotu alebo index.
- Lineárne vyhľadávanie: až n porovnaní;
-1vráť až po prejdení celého zoznamu. - Bublinkové triedenie vymieňa susedov, triedenie výberom presúva minimum zvyšku; obe ≈ n²/2 porovnaní. V praxi
sort/sorted. - Binárne vyhľadávanie: iba v zoradenom zozname, ≈ log₂ n krokov.
- NSD Euklidovým algoritmom (
a, b = b, a % b), prvočísla testom delitelov do √n alebo Eratostenovým sitom.
Úlohy na precvičenie
- Napíš funkciu, ktorá vráti minimum aj maximum zoznamu jedným prechodom.
- Uprav bublinkové triedenie tak, aby triedilo zostupne, a spočítaj, koľko výmen urobilo.
- Napíš funkciu, ktorá vráti zoznam indexov všetkých výskytov hodnoty v zozname.
- Pomocou binárneho vyhľadávania zisti, koľko krokov treba na nájdenie čísla 1 v zozname
list(range(1, 1025)). - Nájdi všetky dvojice prvočísel menších ako 100, ktoré sa líšia o 2 (prvočíselné dvojčatá).
- Napíš funkciu, ktorá prevedie číslo z desiatkovej sústavy do ľubovoľnej sústavy so základom 2 až 9.