A. Permutacja losowa Matematyka dyskretna - wykład - część 2 9. Podstawowe algorytmy kombinatoryczne 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 = c 1 + c 2 2 + c 3 2 3 + c 4 2 3 4 +... = s i=1 c i i! Ponadto 0 c i i. Np. 75 = 1 1! + 1 2! + 0 3! + 3 4!
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, n k 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 (a 1,..., a k ) jest wariacją o numerze m. Jeżeli przyjmiemy, że m = 2 i l (l - liczba nieparzysta), to wariacja o numerze m + 1 ma postać (a 1,..., a i+1 1,..., a k ), gdzie oznacza dodawanie mod 2 Definicja 9.1 Indeksem liczby m przy podstawie n nazywamy taką liczbę naturalną i, że m = n i l, (n l). Indeks oznaczamy następująco: i = ind n 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 = x 1,..., x n. 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, 2 k 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 2 n 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: c 1,..., c k. 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)