Matematyka dyskretna - wykład - część 2 9. Podstawowe
Transkrypt
Matematyka dyskretna - wykład - część 2 9. Podstawowe
Matematyka dyskretna - wykład - część 2 9. Podstawowe algorytmy kombinatoryczne A. Permutacja losowa Załóżmy, że mamy tablice p złożoną z n liczb (ponumerowanych od 0 do n − 1). Aby wygenerować losową permutację tego zbioru należy wykonać n − 2 kroków: 1. losujemy liczbę k ∈ {0, . . . , n − 1} i przestawiamy elementy p[k] i p[n − 1]. 2. losujemy liczbę k ∈ {0, . . . , n − 2} i przestawiamy elementy p[k] i p[n − 2]. (n − 2). losujemy liczbę k ∈ {0, 1} i zamieniamy elementy p[k] i p[1]. Algorytm: Permutacja losowa Dane: n - długość permutacji. Wynik: tablica p zawierająca liczby 0, . . . , n − 1 w przypadkowej kolejności Begin Randomize for k = 0 to n − 1 do p[k] = k Begin l = Random (k) Zamien (p[l], p[k − 1]) End Write(p) End Jak wygenerować wszystkie permutacje zbioru zawierającego n elementów. W algorytmie wykorzystamy fakt, iż każdą liczbę n można przedstawić w postaci sumy: n = c1 + c2 · 2 + c3 · 2 · 3 + c4 · 2 · 3 · 4 + . . . = s X i=1 Ponadto 0 ¬ ci ¬ i. Np. 75 = 1 · 1! + 1 · 2! + 0 · 3! + 3 · 4! ci i! Algorytm: Permutacje 1 Dane: n - długość permutacji Wynik: ciąg zawierający wszystkie permutacje liczb 0, . . . , n − 1 N = n! − 1 for l = 0 to N do { m = l {m − zmienna pomocnicza} for k = 2 to n do { c[k − 2] = m mod k {c ma n − 1 elementów} m = m dir k } for k = 0 to n − 1 do p[k] = k {ciąg wyjściowy w nat. kolejności} for k = n downto 2 do { j = c[k − 2] Zamien (p[j], p[k − 1]) } Write (p) } Kolejny algorytm generuje wszystkie permutacje zbioru liczb 1, . . . , n w porządku antyleksykograficznym. Algorytm: Permutacje 2 Dane: n - długość permutacji (tablica p zawiera liczby 1, . . . , n) Wynik: ciąg permutacji w porządku antyleksykograficznym function odwroc (m) { i = 1, j = m while (i < j) { Zamien (p[i], p[j]) i = i + 1, j = j − 1 } } function antylex (m) { if m = 1 then Write (p) else { for i = 1 to m do { antylex (m − 1) if i < m then Zamien (p[i], p[m]) ; odwroc (m − 1) } } } B. Wariacje z powtórzeniami Niech dany będzie zbiór złożony z n elementów i niech długość wariacji wynosi k. Wówczas jeśli m jest pewną k-elementową wariacją z powtórzeniami zbioru n-elementowego, to m ∈ [0, nk − 1]. W algorytmie wykorzystamy fakt, iż rozwinięcie liczby m przy podstawie n jest k-elementową wariacją z powtórzeniami zbioru {0, . . . , n − 1}. Gdy X = {0, 1} i k = 3 mamy następujące wariacje z powtórzeniami: (0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1) Załóżmy, że (a1 , . . . , ak ) jest wariacją o numerze m. Jeżeli przyjmiemy, że m = 2i · l (l - liczba nieparzysta), to wariacja o numerze m + 1 ma postać (a1 , . . . , ai+1 ⊕ 1, . . . , ak ), gdzie ⊕ oznacza dodawanie mod 2 Definicja 9.1 Indeksem liczby m przy podstawie n nazywamy taką liczbę naturalną i, że m = ni · l, ∼ (n|l). Indeks oznaczamy następująco: i = indn m Algorytm: Wariacje Dane: k - długość wariacji, n - liczność zbioru Wynik: ciąg zawierający wszystkie wariacje z powtórzeniami w[k] - tablica złożona z k liczb, reprezentacja pojedynczej wariacji, stan początkowy w = (0, . . . , 0). skok[k] - tablica k elementów, stan początkowy skok = (1, . . . , 1) function index (m) { i=0 while (m mod n = 0) i = i + 1, m = m dir n index = i } function wariacja { for i = 1 to k do skok[i] = 1 m=0 repeat { Write (w) m=m+1 i = index (m) + 1 if i ¬ k then { w[i] = w[i] + skok[i] if w[i] = 0 then skok[i] = 1 if w[i] = n − 1 then skok[i] = −1 } } until i ¬ k } C. Podzbiory zbioru k-elementowego Niech A będzie k-elementowym podzbiorem zbioru n-elementowego. A więc A ⊂ X = {x1 , . . . , xn }. Aby wygenerować wszystkie podzbiory wykorzystamy funkcję charakterystyczną podzbioru A: χA = (ε1 , . . . , εn ), εi ∈ {0, 1}. Algorytm: Podzbiory 1 Rozwinięcia binarne liczb z przedziału [0, 2k − 1] są funkcjami charakterystycznymi wszystkich podzbiorów zbioru X. Dane: n - liczba elementów w zbiorze Wynik: ciąg wszystkich podzbiorów zbioru {1, . . . , n} S[n] - reprezentacja pojedynczego podzbioru (funkcja charakterystyczna) function Podzbiory { for m = 0 to 2n − 1 { t=m for i = 0 to n { S[i] = t mod 2, t = t dir 2 } Write (S) } } Algorytm: Podzbiory 2 Modyfikacja algorytmu Wariacje - należy przyjąć n = 2. Otrzymamy wszystkie k-elementowe funkcje charakterystyczne zbioru X. Dane: n - liczba elementów w zbiorze Wynik: ciąg wszystkich podzbiorów zbioru {1, . . . , n}, taki że następny różni się od poprzedniego jednym elementem. S[n] - reprezentacja pojedynczego podzbioru (funkcja charakterystyczna) function index (m) { i=0 while (m mod 2 = 0) i = i + 1, m = m dir 2 index = i } function Podzbiory2 { for i = 1 to n S[i] = 0 m=0 repeat { Write (S) m=m+1 i = index (m) + 1 if i ¬ n then S[i] = 1 − S[i] } until i ¬ n } D. Problem plecakowy Dane są przedmioty o wagach: c1 , . . . , ck . Pytanie 1: Czy istnieje taki zestaw przedmiotów, których łączna waga jest równa s. Pytanie 2: Które przedmioty należy zapakować do plecaka, aby ich łączna waga była największa, ale nie przekraczała s. Algorytm: Rozpatrzenie wszystkich podzbiorów zbioru przedmiotów i sprawdzenie, które z tych podzbiorów spełniają warunki zadania. E. Kombinacje k-elementowe. Algorytm: Kombinacje Dane: k - długość kombinacji, n - liczność zbioru Wynik: ciąg k-elementowych kombinacji w porządku leksykograficznym. A[k] - reprezentacja pojedynczej kombinacji. Stan początkowy A = (1, . . . , k) function kombinacje { p=k while p 1 { Write (A) if (A[k] = n) then p = p − 1 else p = k if (p 1) then { for i = k downto p A[i] = A[p] + i − p + 1 } } } F. Podziały liczby - partycje Algorytm: Partycje Dane: n - liczba naturalna Wynik: ciąg wszystkich podziałów liczby n. P [n] - reprezentacja podziału zawierająca składniki podziału function partycje (n, k, r) { if n = 0 then Write (P ) else { for j = r downto 1 { P [k] = j partycje (n − j, k + 1, min(n − j, j)) } } } function main { partycje (n, 1, n) }