Rekurze a rychlé třídění

Podobné dokumenty
Třídění a vyhledávání Searching and sorting

Prioritní fronta, halda

IB111 Úvod do programování skrze Python

DobSort. Úvod do programování. DobSort Implementace 1/3. DobSort Implementace 2/3. DobSort - Příklad. DobSort Implementace 3/3

Algoritmizace prostorových úloh

NPRG030 Programování I, 2018/19 1 / :03:07

IB111 Úvod do programování skrze Python

Spojový seznam. Jan Kybic.

Základy řazení. Karel Richta a kol.

Časová složitost / Time complexity

Náplň. v Jednoduché příklady na práci s poli v C - Vlastnosti třídění - Způsoby (algoritmy) třídění

A4B33ALG 2010/05 ALG 07. Selection sort (Select sort) Insertion sort (Insert sort) Bubble sort deprecated. Quicksort.

Záznam, zásobník a fronta

Algoritmizace řazení Bubble Sort

Bubble sort. příklad. Shaker sort

Rekurzivní algoritmy

Základní datové struktury III: Stromy, haldy

ALG 14. Vícedimenzionální data. Řazení vícedimenzionálních dat. Experimentální porovnání řadících algoritmů na vícedimenzionálních datech

Stromy, haldy, prioritní fronty

Hledání k-tého nejmenšího prvku

Základy algoritmizace a programování

Rekurze. IB111 Úvod do programování skrze Python

Dynamické datové struktury III.

Test prvočíselnosti. Úkol: otestovat dané číslo N, zda je prvočíslem

Dynamické programování

B3B33ALP - Algoritmy a programování - Zkouška z předmětu B3B33ALP. Marek Boháč bohacm11

přirozený algoritmus seřadí prvky 1,3,2,8,9,7 a prvky 4,5,6 nechává Metody řazení se dělí:

B3B33ALP - Algoritmy a programování - Zkouška z předmětu B3B33ALP. Marek Boháč bohacm11

ˇ razen ı rychlejˇ s ı neˇ z kvadratick e Karel Hor ak, Petr Ryˇsav y 20. dubna 2016 Katedra poˇ c ıtaˇ c u, FEL, ˇ CVUT

ALG 09. Radix sort (přihrádkové řazení) Counting sort. Přehled asymptotických rychlostí jednotlivých řazení. Ilustrační experiment řazení

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 21.

Programování: základní konstrukce, příklady, aplikace. IB111 Programování a algoritmizace

3 Vyhledávání, třídící algoritmy

Časová a prostorová složitost algoritmů

Sada 1 - Základy programování

IAJCE Přednáška č. 9. int[] pole = new int[pocet] int max = pole[0]; int id; for(int i =1; i< pole.length; i++) { // nikoli 0 if (Pole[i] > max) {

IB111 Úvod do programování 1 / 62

IB108 Sada 1, Příklad 1 Vypracovali: Tomáš Krajča (255676), Martin Milata (256615)

Maturitní téma: Programovací jazyk JAVA

Michal Krátký. Úvod do programování. Cíl kurzu. Podmínky získání zápočtu III/III

Mimo samotné správnosti výsledku vypočteného zapsaným algoritmem je ještě jedno

Rekurze. Pavel Töpfer, 2017 Programování 1-8 1

ALGORITMY A DATOVÉ STRUKTURY

Rekurze. IB111 Úvod do programování

Třídící algoritmy. Insert Sort. Bubble Sort. Select Sort. Shell Sort. Quick Sort. Merge Sort. Heap Sort.

5 Rekurze a zásobník. Rekurzivní volání metody

Stromy. Strom: souvislý graf bez kružnic využití: počítačová grafika seznam objektů efektivní vyhledávání výpočetní stromy rozhodovací stromy

V případě jazyka Java bychom abstraktní datový typ Time reprezentující čas mohli definovat pomocí třídy takto:

Složitost algoritmů. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava

Stromy. Jan Hnilica Počítačové modelování 14

Algoritmizace Dynamické programování. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Da D to t v o é v ty t py IB111: Datové typy

Algoritmy I. Třídění ALGI 2010/2011

Rekurze. IB111 Základy programování Radek Pelánek

Rekurze. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

TGH07 - Chytré stromové datové struktury

Úvod do programování 10. hodina

Anotace. Informace o praktiku z programování!!! Direktivy překladače Soubory (textové) Quicksort Metoda rozděl a panuj

Binární soubory (datové, typované)

Řazení. Uspořádat množinu prvků obsahujících klíč podle definovaného kriteria.

Obecná informatika. Matematicko-fyzikální fakulta Univerzity Karlovy v Praze. Podzim 2012

Funkce pokročilé možnosti. Úvod do programování 2 Tomáš Kühr

IB111 Základy programování Radek Pelánek 1 / 61

Techniky návrhu algoritmů

Anotace. pointery (pars prima). Martin Pergel,

3 Algoritmy řazení. prvku a 1 je rovněž seřazená.

Anotace. Pointery, dynamické proměnné

TGH07 - Chytré stromové datové struktury

Dynamické programování

4. Rekurze. BI-EP1 Efektivní programování Martin Kačer

Základní informace o předmětu Otázka:

Kombinatorika, výpočty

Asymptotická složitost algoritmů

Algoritmy I, složitost

Složitost algoritmů. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta a kol.

Složitost UPR 2008/09 1

10. Složitost a výkon

Na začátku rozdělíme práci a určíme, které podproblémy je potřeba vyřešit. Tyto

Prohledávání do šířky = algoritmus vlny

Stromy. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta a kol.

TGH05 - Problém za milion dolarů.

Časová složitost algoritmů, řazení a vyhledávání

Rekurze. Jan Hnilica Počítačové modelování 12

Dynamické datové struktury IV.

13. Třídící algoritmy a násobení matic

Inovace a zkvalitnění výuky prostřednictvím ICT Základy programování a algoritmizace úloh Třídění dat. Ing. Hodál Jaroslav, Ph.D. VY_32_INOVACE_26 04

Pole a kolekce. v C#, Javě a C++

VYSOKÉ UČENÍ TECHNICKÉ V BRNĚ BRNO UNIVERSITY OF TECHNOLOGY

SII - Informatika. 1. Atribut relace, jehož hodnota jednoznačně určuje prvek v jiné relaci, se nazývá:

8. Rekurze. doc. Ing. Jiří Vokřínek, Ph.D. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze

5. Dynamické programování

Dekompozice problému, rekurze

Zadání k 2. programovacímu testu

Digitální učební materiál

5. Vyhledávání a řazení 1

Časová složitost algoritmů

Implementace LL(1) překladů

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Transkript:

Rekurze a rychlé třídění Jan Kybic http://cmp.felk.cvut.cz/~kybic kybic@fel.cvut.cz 2016 2017 1 / 54

Rekurze Rychlé třídění 2 / 54

Rekurze Recursion Rekurze = odkaz na sama sebe, definice za pomoci sebe sama Rekurzivní funkce = funkce volá sama sebe (i nepřímo) Řešení problému za pomoci řešení jednodušší varianty téhož problému Obvykle elegantní je potřeba hlídat efektivitu 3 / 54

Příklad: Umocňování Přímá definice x n = Π n i=1x = x x x }{{} n-krát 4 / 54

Příklad: Umocňování Přímá definice Rekurzivní definice x n = Π n i=1x = x x x }{{} n-krát x 0 = 1 x n+1 = x x n for n > 0 4 / 54

Příklad: Umocňování Přímá definice Rekurzivní definice x n = Π n i=1x = x x x }{{} n-krát x 0 = 1 x n+1 = x x n for n > 0 Anatomie rekurze Základní / bázový případ (base case) Převedení problému na jednodušší Odkaz na sebe / Rekurzivní volání 4 / 54

Příklad: Umocňování implementace def power_iterative(x,n): prod=1.0 for i in range(n): prod*=x return prod print(power_iterative(2.0,10)) 1024.0 5 / 54

Příklad: Umocňování implementace def power_iterative(x,n): prod=1.0 for i in range(n): prod*=x return prod print(power_iterative(2.0,10)) 1024.0 def power_recursive(x,n): if n<=0: return 1.0 return x*power_recursive(x,n-1) print(power_recursive(2.0,10)) 1024.0 5 / 54

Příklad: Umocňování implementace (2) def power_recursive(x,n): if n<=0: return 1.0 return x*power_recursive(x,n-1) Stručnější verze def power_recursive2(x,n): return x*power_recursive2(x,n-1) if n>0 else 1.0 Volte verzi, která je pro vás čitelnější. 6 / 54

Příklad: součet pole def sum_array(a): s=0 for x in a: s+=x return s a=[68, 0, 61, 34, 2, 51, 29, 10, 5, 45] print(sum_array(a)) 305 7 / 54

Příklad: součet pole def sum_array(a): s=0 for x in a: s+=x return s a=[68, 0, 61, 34, 2, 51, 29, 10, 5, 45] print(sum_array(a)) 305 Šlo by to napsat bez cyklu? 7 / 54

Příklad: součet pole rekurzivně def sum_array_recursive(a): if len(a)==0: return 0 return a[0]+sum_array_recursive(a[1:]) print(sum_array_recursive(a)) 305 8 / 54

Každý cyklus lze nahradit rekurzí Iterativní verze def count_to(n): for i in range(1,n+1): print(i) count_to(5) 1 2 3 4 5 9 / 54

Každý cyklus lze nahradit rekurzí (2) Rekurzivní verze: def count_to_recursive(n): count_to_recursive_inner(n,1) def count_to_recursive_inner(n,i): if i<=n: print(i) count_to_recursive_inner(n,i+1) count_to_recursive(5) 1 2 3 4 5 10 / 54

Každý cyklus lze nahradit rekurzí (3) Rekurzivně s vnitřní funkcí def count_to_recursive2(n): def count_to_recursive_inner(i): if i<=n: print(i) count_to_recursive_inner(i+1) count_to_recursive_inner(1) count_to_recursive2(5) 1 2 3 4 5 11 / 54

Vnitřní funkce def count_to_recursive2(n): def count_to_recursive_inner(i): if i<=n: print(i) count_to_recursive_inner(i+1) count_to_recursive_inner(1) + Skrytí soukromých funkcí + Sdílení proměnných vnější funkce Nemožnost znovupoužití Nemožnost samostatného odladění 12 / 54

Příklad: Řetězec pozpátku Iterativně def reverse_iterative(s): r="" # result for i in range(len(s)-1,-1,-1): r+=s[i] return r print(reverse_iterative("dobrý večer")) rečev ýrbod 13 / 54

Příklad: Řetězec pozpátku Rekurzivně def reverse_recursive(s): if len(s)==0: return "" return reverse_recursive(s[1:])+s[0] print(reverse_recursive("dobrý večer")) rečev ýrbod 14 / 54

Příklad: Řetězec pozpátku Rekurzivně def reverse_recursive(s): if len(s)==0: return "" return reverse_recursive(s[1:])+s[0] print(reverse_recursive("dobrý večer")) rečev ýrbod Kratší verze: def reverse_recursive2(s): return "" if s=="" else reverse_recursive2(s[1:])+s[0] 14 / 54

Příklad: Řetězec pozpátku Rekurzivně def reverse_recursive(s): if len(s)==0: return "" return reverse_recursive(s[1:])+s[0] print(reverse_recursive("dobrý večer")) rečev ýrbod Kratší verze: def reverse_recursive2(s): return "" if s=="" else reverse_recursive2(s[1:])+s[0] Pythonská verze: print("dobrý večer"[::-1]) rečev ýrbod 14 / 54

Příklad: Číselné soustavy Převeď číslo n v soustavě se základem b na řetězec. 5 10 = 101 2 protože 1 2 2 + 0 2 1 + 1 2 0 = 5. Pokud b > 10 používáme A = 10, B = 11,... Např. 2016 10 = 7E0 16 protože 7 16 2 + 14 16 1 + 0 16 0 = 2016 15 / 54

Příklad: Číselné soustavy Převeď číslo n v soustavě se základem b na řetězec. 5 10 = 101 2 protože 1 2 2 + 0 2 1 + 1 2 0 = 5. Pokud b > 10 používáme A = 10, B = 11,... Např. 2016 10 = 7E0 16 protože 7 16 2 + 14 16 1 + 0 16 0 = 2016 Myšlenka řešení Pokud n < b pak vrať číslici odpovídající n. Jinak Najdi n // b v soustavě b. Přidej nakonec číslici odpovídající n % b 15 / 54

Číselné soustavy implementace Vrať n jako řetězec v číselné soustavě se základem base. def to_str(n, base): assert(n>=0) cislice = "0123456789ABCDEF" if n < base: return cislice[n] return to_str(n // base, base) + cislice[n % base] print(to_str(5,2)) 101 print(to_str(2016,16)) 7E0 16 / 54

Číselné soustavy (3) Nerekurzivní řešení Každé rekurzivní řešení je možné napsat bez rekurze ale může to být těžké. Nutnost zapamatovat si lokální proměnné v jednotlivých voláních na zásobníku (stack). 17 / 54

Číselné soustavy (3) Nerekurzivní řešení Každé rekurzivní řešení je možné napsat bez rekurze ale může to být těžké. Nutnost zapamatovat si lokální proměnné v jednotlivých voláních na zásobníku (stack). def to_str_nonrecursive(n,base): cislice = "0123456789ABCDEF" stack=[n] # hodnoty n n//=base while n>0: stack+=[n] n//=base result="" for m in stack[::-1]: result+=cislice[m % base] return result 17 / 54

Číselné soustavy (4) Nerekurzivní řešení bez zásobníku def to_str_nonrecursive2(n,base): assert(n>=0) cislice="0123456789abcdef" result="" while True: result=cislice[n % base]+result n//=base if n==0: break return result Přidávání na začátek řetězce je pomalé (lineární). Skrytě kvadratický algoritmus. 18 / 54

Permutace Vytiskni všechny permutace prvků dané množiny M. 19 / 54

Permutace Vytiskni všechny permutace prvků dané množiny M. Myšlenka řešení Vezmi každý prvek m i z M. Najdi všechny permutace prvků M\{mi } Ke každé na začátek přidej mi 19 / 54

Permutace implementace def tisk_permutaci(m): """ Vytiskne všechny permutace prvků v m """ tisk_permutaci_acc(m,"") # acc - řetězec přidávaný na začátek def tisk_permutaci_acc(m,acc): if len(m)==0: print(acc,end=", ") else: for i in range(len(m)): tisk_permutaci_acc(m[:i]+m[i+1:],acc+m[i]+" ") 20 / 54

Permutace příklad tisk_permutaci(["a","b","c","d"]) a b c d, a b d c, a c b d, a c d b, a d b c, a d c b, b a c d, b a d c, b c a d, b c d a, b d a c, b d c a, c a b d, c a d b, c b a d, c b d a, c d a b, c d b a, d a b c, d a c b, d b a c, d b c a, d c a b, d c b a, 21 / 54

Příklad: Mince Vypiš všechny způsoby, jak zaplatit x Kč mincemi v hodnotách h = {50, 20, 10, 5, 2, 1} Kč. 22 / 54

Příklad: Mince Vypiš všechny způsoby, jak zaplatit x Kč mincemi v hodnotách h = {50, 20, 10, 5, 2, 1} Kč. Myšlenka řešení Vyber největší minci h i x. Pak jsou dvě možnosti: Použij h i zaplať x h i pomocí h i, h i+1,..., h n a přidej jednu h i. Nepoužij hi zaplať x pomocí h i+1,..., h n 22 / 54

Mince implementace Vytiskni všechny možné způsoby, jak zaplatit x Kč. def zaplat(x): h=[50,20,10,5,2,1] # hodnoty mincí sestupně def doplat(x,m,i): """ m - kolik zaplaceno v počtech mincí i - kterou mincí začít """ if x==0: vytiskni_platbu(m,h) else: if x>=h[i]: # zaplať minci h[i] doplat(x-h[i],m[:i]+[m[i]+1]+m[i+1:],i) if i<len(h)-1: # zaplať menšími, lze-li doplat(x,m,i+1) doplat(x,len(h)*[0],0) # tohle je začátek fce zaplat 23 / 54

Mince implementace (2) def vytiskni_platbu(m,h): """ m - počty mincí, h - hodnoty """ for j in range(len(h)): if m[j]>0: print("%3d*%3dkč" % (m[j],h[j]), end="") print("") 24 / 54

Mince zkouška zaplat(12) 1* 10Kč 1* 2Kč 1* 10Kč 2* 1Kč 2* 5Kč 1* 2Kč 2* 5Kč 2* 1Kč 1* 5Kč 3* 2Kč 1* 1Kč 1* 5Kč 2* 2Kč 3* 1Kč 1* 5Kč 1* 2Kč 5* 1Kč 1* 5Kč 7* 1Kč 6* 2Kč 5* 2Kč 2* 1Kč 4* 2Kč 4* 1Kč 3* 2Kč 6* 1Kč 2* 2Kč 8* 1Kč 1* 2Kč 10* 1Kč 12* 1Kč 25 / 54

Mince rychlejší varianta def zaplat2(x): h=[50,20,10,5,2,1] # hodnoty mincí sestupně def doplat(x,m,i): """ m - kolik zaplaceno v počtech mincí i - kterou mincí začít """ if x==0: vytiskni_platbu(m,h) else: if x>=h[i]: # zaplat minci h[i] m[i]+=1 doplat(x-h[i],m,i) m[i]-=1 # úklid if i<len(h)-1: # zaplať menšími doplat(x,m,i+1) doplat(x,len(h)*[0],0) 26 / 54

Mince zkouška (2) zaplat2(12) 1* 10Kč 1* 2Kč 1* 10Kč 2* 1Kč 2* 5Kč 1* 2Kč 2* 5Kč 2* 1Kč 1* 5Kč 3* 2Kč 1* 1Kč 1* 5Kč 2* 2Kč 3* 1Kč 1* 5Kč 1* 2Kč 5* 1Kč 1* 5Kč 7* 1Kč 6* 2Kč 5* 2Kč 2* 1Kč 4* 2Kč 4* 1Kč 3* 2Kč 6* 1Kč 2* 2Kč 8* 1Kč 1* 2Kč 10* 1Kč 12* 1Kč 27 / 54

Rekurze Rychlé třídění 28 / 54

Merge sort Třídění spojováním Setřiď vzestupně pole a. Základní myšlenka Rozděl pole a na dvě poloviny Každou polovinu setřiď (rekurzivně) Spoj obě setříděné poloviny 29 / 54

Merge sort (2) rozdělování (splitting) 30 / 54

Merge sort (3) spojování (joining) 31 / 54

Merge sort (4) implementace Rozdělování def merge_sort(a): """ Setřídí pole a pomocí merge sort """ if len(a)<=1: # triviální případ return a mid=len(a)//2 # rozdělení pole na dvě left=merge_sort(a[:mid]) right=merge_sort(a[mid:]) return join_arrays(left,right) 32 / 54

Merge sort (4) implementace Spojování def join_arrays(left,right): """ Dvě vzestupně setříděná pole spojí do jednoho """ result=[] # spojení dvou setříděných polí i=0 # index do left j=0 # index do right while i<len(left) and j<len(right): if left[i]<right[j]: result+=[left[i]] i+=1 else: result+=[right[j]] j+=1 result+=left[i:] result+=right[j:] return result # doplnit zbytky 33 / 54

Merge sort (6) příklad a=[random.randrange(100) for i in range(10)] print(a) print(merge_sort(a)) [15, 43, 3, 60, 38, 38, 57, 30, 82, 0] [0, 3, 15, 30, 38, 38, 43, 57, 60, 82] 34 / 54

Merge sort (5) vlastnosti Merge sort potřebuje pomocná pole (není in place). Merge sort je stabilní. 35 / 54

Merge sort (5) vlastnosti Merge sort potřebuje pomocná pole (není in place). Merge sort je stabilní. def merge_sort_inplace(a): """ setřídí pole a na místě """ a[:]=merge_sort(a) 35 / 54

Merge sort (6) složitost Složitost setřídění n prvků Rozdělení lze provést v čase O(n) v každé úrovni rekurze. Spojování lze provést v čase O(n) v každé úrovni rekurze. Úrovní rekurze je log 2 n. Složitost algoritmu merge sort je O(n log n). 36 / 54

Merge sort (6) porovnání rychlosti Empirická složitost 10 2 10 1 10 0 bubble_sort selection_sort insertion_sort merge_sort python_sort time [s] 10-1 10-2 10-3 10-4 10 2 10 3 10 4 N 37 / 54

Quick sort Setřiď vzestupně pole a. Základní myšlenky a vlastnosti Vybere jeden prvek p z pole a pivot. Provedeme částečné setřídění (partitioning): [... ai... }{{} a i p ] p... a j... }{{} a j p Částeční setřídění lze udělat v lineárním čase, O(n). Rekurzivně setřídíme [... a i... ] a [... a j... ]. Třídění na místě, bez pomocného pole (in place). Není stabilní. 38 / 54

Quick sort (2) částečné setřídění Partitioning 39 / 54

Quick sort (3) implementace def quick_sort(a): """ Setřídí pole a na místě """ quick_sort_helper(a,0,len(a)-1) def quick_sort_helper(a,first,last): """ setřídí podpole a[first]..a[last] """ if first < last: m = partition(a,first,last) # vrátí index pivotu quick_sort_helper(a,first,m-1) quick_sort_helper(a,m+1,last) 40 / 54

Quick sort (4) implementace Partitioning Změní a a vrátí index m takový, že všechny prvky před m jsou menší než a m a všechny prvky po m jsou větší než a m. def partition(a,first,right): pivot=a[first] # pivot je první prvek left=first+1 while True: while left<=right and a[left] <= pivot: left+=1 # najdi první zleva větší než pivot while left<=right and a[right] >= pivot: right-=1 # najdi první zprava menší než pivot if right<left: # končíme? a[first],a[right]=a[right],a[first] # pivot na místo return right a[left],a[right]=a[right],a[left] # výměna 41 / 54

Quick sort (5) příklad a=[random.randrange(100) for i in range(10)] print(a) [63, 73, 8, 16, 44, 63, 9, 89, 64, 58] quick_sort(a) print(a) [8, 9, 16, 44, 58, 63, 63, 64, 73, 89] 42 / 54

Quick sort (6) složitost Částeční setřídění lze udělat v lineárním čase, O(n). To platí pro všechna volání na dané úrovni rekurze. Úrovní rekurze je v průměru log 2 n. Průměrná složitost quick sortu je O(n log n). 43 / 54

Quick sort (6) složitost Částeční setřídění lze udělat v lineárním čase, O(n). To platí pro všechna volání na dané úrovni rekurze. Úrovní rekurze je v průměru log 2 n. Průměrná složitost quick sortu je O(n log n). ale... Úrovní rekurze je v nejhorším případě n časová složitost v nejhorším případě je tedy O(n 2 ). Nejhorším případem naší implementace je setříděná posloupnost. Sofistikovanější výběr pivotů pravděpodobnost degenerace podstatně sníží. Quick sort je v průměru jeden z nejrychlejších obecných třídících algoritmů, rychlejší než např. merge sort. Časová složitost v nejhorším případě je ale horší. 43 / 54

Quick sort (6) porovnání rychlosti Empirická složitost 10 2 10 1 10 0 bubble_sort selection_sort insertion_sort merge_sort quick_sort python_sort time [s] 10-1 10-2 10-3 10-4 10 2 10 3 10 4 N 44 / 54

Složitost problému třídění Otázky: Nešlo by najít ještě lepší třídící algoritmus? Jaká je složitost problému třídění? 45 / 54

Složitost problému třídění Otázky: Nešlo by najít ještě lepší třídící algoritmus? Jaká je složitost problému třídění? Výpočetní model: Porovnávací třídící algoritmus (compare-based sorting algorithm) získává informace pouze porovnáváním prvků. Úkolem je najít permutaci k 1,..., k n indexů 1,..., n, tak aby a k1 a k2 a kn. Kolik porovnání a i a j je potřeba? 45 / 54

Dolní odhad složitosti problému třídění Je 2 p možných výsledků p porovnání. Je n! permutací n prvků. Musí platit 2 p n! p log 2 n! 46 / 54

Dolní odhad složitosti problému třídění Je 2 p možných výsledků p porovnání. Je n! permutací n prvků. Musí platit 2 p n! p log 2 n! Neexistuje žádný porovnávací třídící algoritmus, který pro setřídění n prvků potřeboval vždy méně než log 2 n! porovnání 46 / 54

Optimální asymptotická složitost problému třídění Platí, že n log n n + 1 + 1 log n! (n + 1) log + 1 e e tedy asymptoticky log n! n log n a log 2 n! n log 2 n. 47 / 54

Optimální asymptotická složitost problému třídění Platí, že n log n n + 1 + 1 log n! (n + 1) log + 1 e e tedy asymptoticky log n! n log n a log 2 n! n log 2 n. Neexistuje žádný porovnávací třídící algoritmus, jehož asymptotická časová složitost v nejhorším případě by byla lepší než O(n log n). 47 / 54

Optimální asymptotická složitost problému třídění Platí, že n log n n + 1 + 1 log n! (n + 1) log + 1 e e tedy asymptoticky log n! n log n a log 2 n! n log 2 n. Neexistuje žádný porovnávací třídící algoritmus, jehož asymptotická časová složitost v nejhorším případě by byla lepší než O(n log n). Poznámky: Optimální časovou složitost má merge sort (třídění spojováním), heap sort,... Algoritmus quick sort je často v praxi rychlejší, ale má optimální časovou složitost pouze v průměru, složitost nejhoršího případu je O(n 2 ). Tato analýza platí pouze pro porovnávací algoritmy. 47 / 54

Radix sort Číslicové/přihrádkové třídění Vlastnosti Třídění n přirozených čísel (nebo řetězců) pevné délky k. Časová složitost O(nk). Může být implementován jako stabilní. Potřebuje pomocnou paměť. 48 / 54

Radix sort Číslicové/přihrádkové třídění Vlastnosti Třídění n přirozených čísel (nebo řetězců) pevné délky k. Časová složitost O(nk). Může být implementován jako stabilní. Potřebuje pomocnou paměť. Základní myšlenka Mějme přihrádku pro každou možnou číslici (0... 9). Postupně od nejméně významného řádu Každý prvek přidáme do přihrádky dle číslice daného řádu. Obsah přihrádek za sebe zřetězíme v pořadí dle hodnoty číslic. 48 / 54

Radix sort implementace (1) Pomocné funkce: def num_digits(a): """ počet číslic v desítkovém zápisu čísla a """ num=1 while a>10: a//=10 num+=1 return num def digit(a,n): """ n-tá číslice zprava čísla a, počítáno od nuly""" return a//(10**n) % 10 Soubor radix_sort_experiments.py 49 / 54

Radix sort implementace (2) Hlavní smyčka: radix_sort_integers setřídí pole přirozených čísel s omezeným počtem číslic def radix_sort_integers(a): max_digits=num_digits(max(a)) # max. počet číslic for i in range(max_digits): a=sort_by_digit(a,i) return a 50 / 54

Radix sort implementace (2) Vnitřní smyčka: def sort_by_digit(a,i): """ setřídí pole a dle i-té číslice """ prihradka=[ [] for j in range(10) ] for x in a: # rozhoď do přihrádek c=digit(x,i) # číslice pro třídění prihradka[c]+=[x] r=[] # spoj přihrádky for p in prihradka: r+=p return r Časová složitost O(nk). 51 / 54

Radix sort příklad a=[random.randrange(100) for i in range(10)] print(a) [47, 71, 96, 75, 62, 23, 9, 68, 86, 10] print(radix_sort_integers(a)) [9, 10, 23, 47, 62, 68, 71, 75, 86, 96] 52 / 54

Radix sort porovnání rychlosti Empirická složitost 10 2 10 1 10 0 merge_sort quick_sort radix_sort_integers python_sort time [s] 10-1 10-2 10-3 10-4 10 3 10 4 10 5 10 6 N 53 / 54

Shrnutí Rekurze Rekurzivní a iterativní formulace jsou ekvivalentní. Rekurzivní formulace bývá elegantní a stručná. Rekurzivní implementace může být neefektivní. Třídění Jednoduché třídící algoritmy mají složitost O(n 2 ). Optimální složitost porovnávacích třídících algoritmů je O(n log n), např. merge sort. Typicky bývá nejrychlejší quick sort, až na degenerované případy. V případech omezené vstupní množiny lze použít radix sort se složitostí O(n). 54 / 54