Test prvočíselnosti Úkol: otestovat dané číslo N, zda je prvočíslem 1. zkusit všechny dělitele od 2 do N-1 časová složitost O(N) cca N testů 2. stačí zkoušet všechny dělitele od 2 do N/2 (větší dělitel neexistuje) časová složitost opět O(N) cca N/2 testů, tedy o něco lepší 3. stačí zkoušet všechny dělitele od 2 do N (má-li N vlastního dělitele, musí mít i dělitele z tohoto intervalu) časová složitost O( N) cca N testů, asymptoticky lepší 4. stačí zkoušet dělitele od 2 do N, do nalezení prvního asymptotická časová složitost opět O( N), většinou se ale vykoná o dost méně testů - v nejhorším případě se vykoná N testů časová složitost O( N) - v nejlepším případě stačí pouze jeden test časová složitost O(1), tzn. konstantní Pavel Töpfer, 2018 Programování 1-4 1
Typ Boolean var B: boolean; - pouze dvě možné hodnoty: false a true - do proměnné typu boolean lze dosadit hodnotu výrazu typu boolean - výrazy typu boolean = logické výrazy * jednoduché: konstanty, proměnné, funkce typu boolean, relace (pomocí relačních operátorů) * složené: z jednoduchých pomocí logických spojek a ( ) - standardní funkce typu boolean * odd(n) N je liché (parametr N celočíselný) * později budou další (např. eof, eoln u souborů) - výraz typu boolean lze použít: * dosadit jeho hodnotu do proměnné typu boolean * na místě podmínky (if, while, until) Pavel Töpfer, 2018 Programování 1-4 2
Příklady: B := Pocet = 100; {vyhodnotí se podmínka Pocet = 100 a výsledná logická hodnota se dosadí do proměnné B typu Boolean} if Pocet = 100 then B := true else B := false; {má stejný význam} if B and (X > 0) then {složený výraz na místě podmínky} while Pokracovat do begin end; {Pokracovat je proměnná typu Boolean, v těle cyklu se za určitých podmínek nastavuje na false, aby cyklus skončil} while true do begin end; {věčný cyklus, v těle cyklu musí být zajištěno ukončení výpočtu pomocí break} Příklad: Test prvočíselnosti (2. verze) Pavel Töpfer, 2018 Programování 1-4 3
Pole type JménoTypu = array [DolníMez.. HorníMez] of TypPoložek - dolní mez a horní mez jsou celočíselné konstanty - typ položek libovolný, ale všechny položky jsou stejného typu const Max = 10; type T = array [1..Max] of integer; var P: T; type T1 = array [-10..10] of real; var P1, Q1: T1; {dvě pole stejného typu} Zkratka deklarace proměnné bez pojmenování typu, definice nového typu je součástí deklarace proměnných: var P1, Q1: array [-10..10] of real; Pavel Töpfer, 2018 Programování 1-4 4
Indexování P[3] := 17; read(p1[-4]); S proměnnou typu pole lze bez indexování provádět jedinou operaci, a to dosazení mezi dvěma proměnnými téhož typu: Q1:=P1; Pole se může indexovat proměnnou (obecně dokonce libovolným celočíselným výrazem) možnost v cyklu vykonat jistou akci se všemi složkami pole. Typicky se využívá for-cyklus a indexování prvků pole jeho řídicí proměnnou: for I:=1 to Max do read(p[i]); {načtení pole po složkách} S:=0; for I:=1 to Max do S:=S+P[I]; {součet všech prvků pole} Pavel Töpfer, 2018 Programování 1-4 5
Pozor na hodnotu indexu mimo deklarovaný rozsah reakce podle nastavení přepínače $R: {$R+} provádí se kontrola při výpočtu běhová chyba přetečení indexu (Range check error) {$R-} kontrola se neprovádí kratší a rychlejší přeložený kód, ale logická chyba (chybný výpočet bez upozornění, může třeba dojít ke změně jiné proměnné) Standardní výchozí nastavení: bez kontrol Doporučení: dokud není program odladěn, vždy zapínat! Nejčastější příčina problémů, pokud se program chová zdánlivě nesmyslně. Pavel Töpfer, 2018 Programování 1-4 6
Vyhodnocení polynomu v bodě p(x) = a n x n + a n-1 x n-1 + a n-2 x n-2 + a 1 x + a 0 n stupeň polynomu a 0,, a n koeficienty (reálné konstanty) x proměnná, za niž dosazujeme různé hodnoty Přímý výpočet podle uvedeného předpisu počet násobení: n + (n-1) + (n-2) + + 1 = n.(n+1)/2 počet sčítání: n časová složitost algoritmu: O(n 2 ) Hornerovo schéma p(x) = ( ((a n x + a n-1 ).x + a n-2 ).x + +a 1 ).x + a 0 počet násobení: n počet sčítání: n časová složitost algoritmu: O(n) Pavel Töpfer, 2018 Programování 1-4 7
Eratosthenovo síto nalezení všech prvočísel od 2 do N Princip: v řadě čísel od 2 do N postupně vyškrtáváme všechny násobky jednotlivých prvočísel, co nebude vyškrtnuto, je prvočíslo. Realizace: Síto v programu reprezentujeme polem prvků typu Boolean, index pole určuje číslo od 2 do N, hodnota prvku síta říká, zda je prvočíslem. Pavel Töpfer, 2018 Programování 1-4 8
Vyhledávání v poli Úkol: zjistit, zda se v poli nachází daná hodnota a kde (pokud je tam vícekrát, chceme první výskyt) Algoritmus: jeden sekvenční průchod polem časová složitost O(N) var A: array [1..N] of T; X: T; {hledaná hodnota} 1. for-cyklus J := 0; for I := 1 to N do if A[I] = X then J := I; if J = 0 then { X v poli A není } else { X nalezen A[J] } Jednoduché, ale nešikovné (cyklus pokračuje i po nalezení X) Pavel Töpfer, 2018 Programování 1-4 9
2. for-cyklus s výskokem J := 0; for I := 1 to N do if A[I] = X then begin J := I; break end; if J = 0 then { X v poli A není } else { X nalezen A[J] } Obdobné řešení, ale cyklus zbytečně nepokračuje po nalezení X. Použitelné jen v implementacích, které mají break. Pavel Töpfer, 2018 Programování 1-4 10
3. while-cyklus I := 1; while (I <= N) and (A[I] <> X) do inc(i); if I > N then { X v poli A není } else { X nalezen A[I] } Vše řeší vhodně zvolená složená podmínka, ale POZOR! Není-li prvek X v poli A obsažen, může nastat běhová chyba přetečení indexu, neboť v podmínce se testuje A[I] pro I=N+1. Korektní řešení v případě zkráceného vyhodnocování {$B-}. Dva způsoby vyhodnocování logických výrazů - volba režimu přepínačem $B: {$B+} úplné vyhodnocování nejprve všechny podvýrazy (norma) {$B-} zkrácené vyhodnocování zleva doprava dokud není rozhodnuto o výsledku (implicitní nastavení v TP) Pavel Töpfer, 2018 Programování 1-4 11
4. cyklus řízený proměnou typu boolean var Dalsi: boolean; {zda zpracovávat další prvek} I := 1; Dalsi := true; while Dalsi do if A[I] = X then begin Dalsi := false; { X nalezen A[I] } end else if I = N then begin Dalsi := false; { X v poli A není } end else inc(i); Pavel Töpfer, 2018 Programování 1-4 12
5. vyhledávání pomocí zarážky zjednodušení podmínky ve while-cyklu var A: array [1..N+1] of T; {prvek A[N+1] pro uložení zarážky} X: T; {hledaná hodnota} A[N+1] := X; {uložení zarážky} I := 1; while A[I] <> X do inc(i); if I > N then { X v poli A není } else { X nalezen A[I] } Hodnota X je v poli A vždy nalezena pokud ale I > N, tak až na místě zarážky, tzn. původně v poli A[1..N] nebyla. Pavel Töpfer, 2018 Programování 1-4 13
Vyhledávání A) sekvenční průchod daty velikosti N časová složitost O(N) B) půlení intervalů (binární vyhledávání) - data musí být uspořádaná - vždy porovnat hledanou hodnotu s prostředním prvkem zkoumaného úseku, polovinu úseku zahodit - postupně dostáváme úseky délky N, N/2, N/4, N/8,, 1 - po K krocích zbývá úsek velikosti N/2 K, hledáme K takové, aby N/2 K = 1 počet půlení K = log 2 N, tedy časová složitost algoritmu O(log N) Příklad: pražský telefonní seznam bytových stanic - cca 430 000 jmen (bylo v roce 1995) - rychlost hledajícího člověka 1 jméno za sekundu - sekvenční hledání: 5 dní a nocí x binární hledání: 20 sekund Pavel Töpfer, 2018 Programování 1-4 14
Binární vyhledávání - vyhledávání v uspořádaném poli Algoritmus: metoda půlení intervalů časová složitost O(log N) var A: array [1..N] of T; {A[1]<A[2]< <A[N]} X: T; {hledaná hodnota} i,j,k: integer; {pomocné indexy} i := 1; j := N; {meze zkoumaného úseku} repeat k := (i+j) div 2; {index prostředního prvku} if X > A[k] then i := k+1 else j := k-1 until (A[k] = X) or (i > j); if X = A[k] then {X nalezen A[k]} else {X v poli A není} Pavel Töpfer, 2018 Programování 1-4 15
Třídění čísel v poli = vnitřní třídění (řazení) Úkol: uspořádat prvky pole podle velikosti (od nejmenšího po největší). Přímé metody - jednoduchý zápis programu - časová složitost O(N 2 ) vhodné jen pro malá data - třídí na místě (tzn. nepotřebují další datovou strukturu velikosti N) Další metody budou později - rekurzivní quicksort a mergesort, třídění haldou (heapsort) - časová složitost O(N log N) Pavel Töpfer, 2018 Programování 1-4 16
Příklad: rychlost výpočtu 1 porovnání dat za 1 mikrosekundu (to je hodně pomalý procesor) N algoritmus O(N 2 ) algoritmus O(N. log N) 100 0.01 s 0.6 ms 100 000 2.8 hod 1.7 s Závěr: - pro malá N není rozdíl doby výpočtu podstatný - pro hodně velká N může být zajímavý, i když O(N 2 ) je vcelku příznivá složitost algoritmu Pavel Töpfer, 2018 Programování 1-4 17
Třídění výběrem (přímý výběr, SelectSort) Algoritmus: Pole se dělí na setříděný úsek (vlevo) a nesetříděný úsek (vpravo). Na začátku tvoří všechny prvky nesetříděný úsek. V nesetříděném úseku pole se vždy najde nejmenší prvek a vymění se s prvním prvkem tohoto úseku, tím se nesetříděný úsek zleva zkrátí o jeden prvek. Realizace: na místě v jediném poli minima se postupně ukládají zleva, výměnou s původním hodnotami. 7 4 2 9 5 modře hotovo (setříděný úsek) 2 4 7 9 5 červeně minimum ze zbývajících hodnot 2 4 7 9 5 2 4 5 9 7 2 4 5 7 9 Pavel Töpfer, 2018 Programování 1-4 18
const N = 1000; type Pole = array [1..N] of integer; var A: Pole; {tříděné pole} i, j, k: integer; {indexy prvků} x: integer; {pro výměnu prvků} for i:=1 to N-1 do begin {umístit číslo na pozici i v poli A} k:=i; for j:=i+1 to N do {vyhledání minima v úseku A[i..N]} if A[j] < A[k] then k:=j; if k > i then {výměna prvků s indexy i, k} begin x:=a[k]; A[k]:=A[i]; A[i]:=x end end Pavel Töpfer, 2018 Programování 1-4 19
Totéž ve tvaru procedury: procedure PrimyVyber(var A: Pole); var i, j, k: integer; {indexy prvků} x: integer; {pro výměnu prvků} begin for i:=1 to N-1 do {umístit číslo na pozici i} begin k:=i; for j:=i+1 to N do {vyhledání minima} if A[j] < A[k] then k:=j; if k > i then {výměna prvků s indexy i, k} begin x:=a[k]; A[k]:=A[i]; A[i]:=x end end end; {procedure PrimyVyber} Pavel Töpfer, 2018 Programování 1-4 20