# AISD - lista 7 | 1 | 2 | 3 | 4 | 5 (Z) | 6 | 7 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:| | 2 | | 1 | 1 | | | Cormen 274 | ### ZAD 1 ![](https://i.imgur.com/jYuAcgO.png) Czas wykonania operacji na słowniku wyraża się wzorem $$ \sum_i p_i \cdot h_i\\ h-wysokośc, czas\ oczekiwany\ na\ wykonanie\ polecenia\\ p-prawdopodobieństow\ odpowaidającej\ liczbie\ zapytań $$ Dążymy do tego, aby wartośc ta była jak najmniejsza. Do rozwiązania tego zadania wykorzystamy programowanie dynamiczne, a by rozpatrzeć każde możliwe drzewo. Oznaczmy $T_{i,j}$ jako minimalny koszt drzewa zbudowanego z elementów o indeksach z zakresu $<i,j>$. Przypadki brzegowe: Dla $T_{i,i}$ czas oczekiwania będzie równy $p_i$. $$ T_{i,j} = \min_{i\le k\le j} ((T_{i,k-1} + \sum_{l=i}^{k-1}p_l) + p_k + (T_{k+1,j}+\sum_{l=k+1}^{j}p_l))\\ \sum_{l = i}^{j}p_l + \min_{i\le k\le j} (T_{i,k-1}+ T_{k+1,j}) $$ Sumy prawdopodoieństw możemy liczyć za pomocą sum prefiksowych. Tablica n x n będziemy wypełniali wstępująco, zaczynając od przypadków brzegowych. Kolejno dla $T_{i, j}$ gdzie $|i-j| = 1$, potem gry róznica ta jest równa 2,3,4 itd... Tablica jest kwadratowa, a wyliczenie każdej wartość zajmuje nam czas liniowy, zatem $O{n^3}$ Odzyskanie informacji o tym jak zbudowane jest drzewo jest proste. Wystarczy stworzyć osobną tablicę n-elemetnową i na bieżąco zapisywać w niej wyniki. ### ZAD 2 ![](https://i.imgur.com/J8ms742.png) ### ZAD 3 ![](https://i.imgur.com/T2UHgzS.png) Do tablicy n-elementowej wstawiamy n kluczy. Szansa, że wybrany klucz nie trafi do jakiejś listy wynosi $(n-1)/n$ (bo prawdopodobieństwo, że do niej trafił jest $1/n$ czyli mamy $1 - 1/n$). Prawdopodobieństwo, ze klucz trafi do listy jest dla kazdej listy takie samo więc oczekiwana ilość pustych list wynosi $E = n * P$ (z liniowości wartości oczekiwanej) gdzie P to prawdopodobieństwo tego, że każda lista jest pusta (prawdopodobieństwo łączne) List jest n więc $P=((n-1)/n)^n$ (wybrany klucz nie trafi do wybranej listy n razy) Stąd mamy że $E = n * ((n-1)/n)^n$ ### ZAD 4 ![](https://i.imgur.com/NqMBUtw.png) **Niezminnik kredytow:** Każdy korzeń ma odłożona jednostkę, ponad to każdy wierzchołek, który utracil syna ma tyle odłożonych jednostek co utraconych synów. **INSERT** ```python= def insert(H, x): x.p = nil x.child = nil x.mark = 0 if H.min == nil: lista korzeni zawierająca jedynie x H.min = x else wstaw x na listę korzeni if x.key < H.min.key H.min = x H.n = H.n + 1 ``` Podczas wykonywania tej operacji: - dołączamy do listy już gotowe drzewo (1 operacja) - porównujemy z min i ew przepinamy wskaźnik (0 lub 1 operacja) - przydzielamy jedną jednostkę kredytową korzeniowi w nowym drzewie i jedną już wykorzystaliśmy na dopisanie nowego elementu Koszt zamortyzowany to $O(1)$. **FINDMIN** ```python= def findmin(H): return H.min ``` Mamy wskaźnik, który wskazuje na najmniejszy element. Wytarczy więc zobaczyć na co wskazuje. Nic nie zmieniamy w kopcu, zatem koszt zamortyzowany to $O(1)$. **DECSREASE KEY** ```python= def decrease_key(H, x, k): x.key = k y = x.p if y != nil and x.key < y.key: #jeżeli ma rodzica i jest zaburzony porządek cut(H, x, y) cas_cut(H, y) if x.key < H.min.key: H.min = x def cut(H, x, y): usuń x z listy synów y dodaj x do listy korzeni x.p = nil x.mark = false def cas_cut(H, y): z = y.p if z != nil: if y.mark == 2: y.mark == 3 else if u.mark == 1: y.mark = 2 else if y.mark = 0: y.mark = 1 else cut(H, y, z) cas_cut(H, z) ``` Ucinym i przekładamy poddrzewo jako osobny kopiec. Jemy i jego ojcu przydzielamy po 1 jednostce kredytowej. Musimy również sprawdzić czy przodek nie ma uciętego trzeciego syna, czyli czy może przypadkiem nie otrzymał właśnie trzeciej jednostki kredytowej. Jeśli tak to wykonujemy odcięcie kaskadowe: przodka wraz z jednostkami kredytowymi przenosimy go jako nowy kopiec w liście, za co płacimy 1 środek, a ojcu oddajemy ostatni 1 środek. Zauważmy, że odcięcie nie ma wpływu na koszt całości. Zatem koszt zamortyzowany całej operacji to $O(1)$. **MELD** ```python= def meld(H1, H2): stwórz nowy pusty kopiec H H.min = H1.min łączymy liste korzeni kopców H2 i H1 if H1.min == nil or (H2.min != nil and H2.min.key < H1.min.key): H.min = H2.MIN H.n = H1.n + H2.n return H ``` W przypadku złączenie dwóch kopców przpinamy jedynie wskaźniki. Zatem koszt zamortyzowany całej operacji to $O(1)$. **DELETMIN** ```python= def deletemin(H): z = H.min if z != nil: dla każdego syna z: x = aktualnie rozpatrywany syn z dodaj x do listy korzeni x.p = nil usuń z z listy korzeni H if z == z.right #jeśli jedno elementowa H.min = nil else H.min = z.right znalezienie minimum H.n = H.n - 1 ``` Wykonując operację deletemin musimy zadbać o usunięcie minimum, przepięcia synów minimum, przydzielenia im jednostki kredytowej oraz znalezienie nowego minimum. Przepięci oraz przydzielenie jednoskti kredytowej zajmie nam $O(lgn)$, ponieważ w najgorszym przypadku mamy $lgn$ dzieci. Po usunięciu minimum musimy skompaktować wszystkie drzew, a nie wiemy ile ich jest. W każdym z teych drzew jest odłożona jednostka kredytowa, a przez każde drzewo wsytarczy przejśc tylko raz, więc kompaktowanie opłacamy z jednostek kredytowych, a nastepnie pozostaje nam conajwyżej logn drzew w kopcu Fibonacciego. Połączenie drzew o jednakowym stopniu polega na tym, że to drzewo o mniejszym korzeniu przyłączamy drzewo o większym korzeniu. Musimy zatem przejść jeszcze po (w najgorszym przpypadku) $lgn$ korzeniach, a by wybrac minimum. Zatem czas zamortyzowany $O(lgn)$. ### ZAD 5 ![](https://i.imgur.com/Yxv6dkZ.png) ### ZAD 6 ![](https://i.imgur.com/Rjn6oof.png) ### ZAD 7 ![](https://i.imgur.com/gL0kqi9.png) współczynnik zapełnienia $\alpha = n/m$ W adresowniu otwartym na pozycję przypada co najwyżej 1 element więc n<=m czyli $\alpha <= 1$ Dla zadanego rozkładu prawdopodobieństwa wystapienia kluczy oraz zachowania funkcji haszujacej na kluczach prwadopodobieństwo wystapienia kazdego ciągu kontrolnego jest takie samo. Lemat 1. (z wykładu) ![](https://i.imgur.com/zJn1H75.jpg) A.12 ![](https://i.imgur.com/xfe86Al.jpg) Dowód: Wyszukanie elementu k wymaga wykonania tego samego ciągu sprawdzeń jak wstawianie. Z lematu 1 mamy, że jeśli k był i+1-wszym elementem wstawionym do tablicy to oczekiwana liczba sprawdzeń potrzebnych do jego odszukania jest nie większa niż $1/(1 - i/m) = m/(m-i)$. Uśredniając wszystkie n kluczy znajdujace się w tablicy otrzymujemy oczekiwaną liczbę sprawdzeń: $1/n \sum_{i=0}^{n-1} m/(m-i) = \\ m/n \sum_{i=0}^{n-1} 1/(m-i) =\\ 1/\alpha \sum_{k=m-n+1}^{m} 1/k <= (z A.12)\\ 1/\alpha \int_{m-n}^m (1/x) dx\\ = 1/\alpha * ln(m/(m-n))=\\ 1/\alpha * ln(1/(1-\alpha)$