Wykład 3 Metoda dziel i zwyciężaj 1
Wprowadzenie Technika konstrukcji algorytmów dziel i zwyciężaj. przykładowe problemy: Wypełnianie planszy Poszukiwanie (binarne) Sortowanie (sortowanie przez łączenie - merge sort). 2
Wypełnianie planszy Zadanie: dysponując klockami oraz planszą 2 n x2 n z brakującym polem: Wypełnić plansze w całości: 3
Wypełnianie planszy: przypadki trywialne (n = 1) Przypadek trywialny (n = 1): wypełniamy plansze jednym klockiem: Idea dla rozwiązania problemu doprowadzić rozmiar zadania do przypadku trywialnego, który umiemy rozwiązać 4
Wypełnianie planszy : podział zadania Oryginalną planszę dzielimy na 4 części Dostajemy problemy o rozmiarze 2 n-1 x2 n-1 Ale: trzy z nich nie są podobne do oryginalnego (plansze nie mają brakującego pola)! 5
Wypełnianie planszy : podział zadania pomysł: umieszczamy jeden klocek w środku planszy i dokonujemy podziału na 4 części Teraz otrzymujemy 4 plansze o rozmiarach 2 n-1 x2 n-1. Każda z planszy ma brakujące pole 6
Wypełnianie planszy : algorytm INPUT: n plansza 2 n x2 n, L pozycja brakującego pola. OUTPUT: wypełniona plansza Tile(n, L) if n = 1 then przypadek trywialny wypełnij jednym klockiem return umieść jeden klocek w środku planszy podziel planszę na 4 równe części Niech L1, L2, L3, L4 oznaczają pozycje 4 brakujących pól Tile(n-1, L1) Tile(n-1, L2) Tile(n-1, L3) Tile(n-1, L4) 7
Dziel i zwyciężaj Metoda konstrukcji algorytmów Dziel i zwyciężaj : Jeśli problem jest na tyle mały, że umiesz go rozwiązać - zrób to. Jeśli nie to: Podział: Podziel problem na dwa lub więcej rozdzielnych podproblemów Rozwiązanie: Wykorzystaj metodę rekurencyjnie dla rozwiązania tych podproblemów Łączenie: połącz rozwiązania podproblemów tak, aby rozwiązać oryginalny problem 8
Wypełnianie planszy : Dziel i zwyciężaj Wypełnianie jest przykładem algorytmu dziel i zwyciężaj : w wypadku trywialnym (2x2) po prostu wypełniamy planszę, lub: Dzielimy planszę na 4 mniejsze części (wprowadzając wypełnione miejsce w rogu, przez umieszczenie centralnie jednego klocka) Rozwiązujemy problem rekursywnie stosując tą samą metodę Łączymy części umieszczając klocek w środku planszy 9
Poszukiwanie binarne Odnaleźć liczbę w posortowanej tablicy: Przypadek trywialny tablica jest jednoelementowa Albo dzielimy tablice na dwie równe części i rozwiązujemy zadanie osobno dla każdej z nich INPUT: A[1..n] posortowana niemalejąco tablica liczb, s liczba. OUTPUT: indeks j taki, że A[j] = s. NIL, jeśli "j (1 j n): A[j] s Binary-search(A, p, r, s): if p = r then if A[p] = s then return p else return NIL q (p+r)/2 ret Binary-search(A, p, q, s) if ret = NIL then return Binary-search(A, q+1, r, s) else return ret 10
Rekurencja Czas działania algorytmu z odwołaniami rekursywnymi można opisać poprzez rekurencję Równanie/nierówność opisująca funkcję poprzez jej wartości dla mniejszego argumentu Przykład: poszukiwanie binarne Tn ( ) (1) if n 1 2 T( n / 2) (1) if n 1 Po rozwiązaniu daje to złożoność O(n)! taką samą jak dla metody naiwnej 11
Poszukiwanie binarne (poprawione) T(n) = (n) nie lepiej niż dla metody siłowej! Poprawa: rozwiązywać zadanie tylko dla jednej połowy tablicy INPUT: A[1..n] posortowana niemalejąco tablica liczb, s liczba. OUTPUT: indeks j taki, że A[j] = s. NIL, jeśli "j (1 j n): A[j] s Binary-search(A, p, r, s): if p = r then if A[p] = s then return p else return NIL q (p+r)/2 if A[q] s then return Binary-search(A, p, q, s) else return Binary-search(A, q+1, r, s) 12
Czas działania metody Tn ( ) (1) if n 1 T ( n / 2) (1) if n 1 T(n) = (lg n)! 13
Sortowanie przez łączenie (merge sort) Podziel: Jeśli S posiada przynajmniej dwa elementy (1 lub 0 elementów przypadek trywialny), podziel S na dwie równe (z dokładnością do 1 elementu) części S 1 i S 2. (tj. S 1 zawiera pierwsze n/2 elementów, a S 2 kolejne n/2 ). Zwyciężaj: posortuj sekwencje S 1 i S 2 stosując Merge Sort. Połącz: Połącz elementy z dwóch posortowanych sekwencji S 1 i S 2 w sekwencję S zachowaniem porządku 14
Algorytm Merge Sort Merge-Sort(A, p, r) if p < r then q (p+r)/2 Merge-Sort(A, p, q) Merge-Sort(A, q+1, r) Merge(A, p, q, r) Merge(A, p, q, r) wybieramy mniejszy z dwóch elementów na początku sekwencji A[p..q] oraz A[q+1..r] i wkładamy go do sekwencji wynikowej, przestawiamy odpowiedni znacznik. Powtarzamy to aż do wyczerpania się elementów. Rezultat kopiujemy do A[p..r]. 15
Sortowanie przez łączenie - 1 16
Sortowanie przez łączenie - 2 17
Sortowanie przez łączenie - 3 18
Sortowanie przez łączenie - 4 19
Sortowanie przez łączenie - 5 20
Sortowanie przez łączenie - 6 21
Sortowanie przez łączenie - 7 22
Sortowanie przez łączenie - 8 23
Sortowanie przez łączenie - 9 24
Sortowanie przez łączenie - 10 25
Sortowanie przez łączenie - 11 26
Sortowanie przez łączenie - 12 27
Sortowanie przez łączenie - 13 28
Sortowanie przez łączenie - 14 29
Sortowanie przez łączenie - 15 30
Sortowanie przez łączenie - 16 31
Sortowanie przez łączenie - 17 32
Sortowanie przez łączenie - 18 33
Sortowanie przez łączenie - 19 34
Sortowanie przez łączenie - 20 35
Sortowanie przez łączenie - 21 36
Sortowanie przez łączenie - 22 37
Sortowanie przez łączenie podsumowanie Sortowanie n liczb jeśli n=1 trywialne rekursywnie sortujemy 2 ciągi n/2 i n/2 liczb łączymy dwa ciągi w czasie (n) Strategia Podział problemu na mniejsze, ale analogiczne podproblemy Rekursywne rozwiązywanie podproblemów Łączenie otrzymanych rozwiązań 38
Sortowanie przez łączenie czas działania Czas działania algorytmu może być reprezentowany przez następującą zależność rekurencyjną: Tn ( ) (1) if n 1 2 T( n / 2) ( n) if n 1 Po rozwiązaniu dostajemy: T ( n) ( nlg n) 39
Wieże Hanoi Mamy 3 wieże oraz stos 64 dysków o zmniejszających się średnicach umieszczonych na pierwszej wieży Potrzebujemy przenieść wszystkie dyski na inną wieżę Zabronione jest położenie dysku większego na mniejszym W każdym kroku wolno mam przenieść tylko jeden dysk 40
Wieże Hanoi 41
Rozwiązanie rekursywne 42
Algorytm rekursywny INPUT: n ilość dysków, a, b, c wieże, wieża a zawiera wszystkie dyski. OUTPUT: a, b, c wieże, wieża b zawiera wszystkie dyski Hanoi(n, a, b, c) if n = 1 then Move(a,b); else Hanoi(n-1,a,c,b); Move(a,b); Hanoi(n-1,c,b,a); Poprawność algorytmu łatwo pokazać przez indukcję względem n. 43
Ilość kroków n M(n) Ilość kroków M(n) potrzebnych do rozwiązania problemu dla n dysków spełnia zależność rekurencyjną M(1) = 1 M(n) = 2M(n-1) + 1 1 1 2 3 3 7 4 15 5 31 44
Ilość kroków Rozwijając tę zależność dostajemy M(n) = 2M(n-1) + 1 = 2*[2*M(n-2)+1] + 1 = 2 2 * M(n-2) + 1+2 = 2 2 * [2*M(n-3)+1] + 1 + 2 = 2 3 * M(n-3) + 1+2 + 2 2 = Po k krokach M(n) = 2 k * M(n-k) + 1+2 + 2 2 + + 2 n-k-1 Dla k = n-1 M(n) = 2 n-1 * M(1) + 1+2 + 2 2 + + 2 n-2 = 1 + 2 + + 2 n-1 = 2 n -1 45