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)
}