Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego...

6
Programowanie dynamiczne Ciąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n 2 Elementy tego ciągu stanowią liczby naturalne tworzące ciąg o takiej wlasności, że kolejny wyraz (z wyjątkiem dwóch pierwszych) jest sumą dwóch poprzednich (tj. 1,1,2,3,5, 8,13, …). Obliczanie czwartego elementu ciągu Fibonacciego Każde pogrubione wyrażenie stanowi problem elementarny. Problem o rozmiarze n 2 zostaje rozbity na dwa problemy o mniejszym stopniu skomplikowania: n-1 i n-2. Proces dekompozycji zatrzymuje się na przypadkach elementarnych. Zaimplementuj powyższy algorytm. Wada Można dostrzec, że znaczna część obliczeń jest wykonywana więcej niż jeden raz (np. cala galęź zaczynająca się od fib(2) jest wręcz zdublowana). Z powyższą wadą radzi sobie programowanie dynamiczne. koncepcja: dla danego problemu stwórz rekurencyjny model jego rozwiązania (wraz z jednoznacznym określeniem przypadków elementarnych). Stwórz tablicę w której będzie można zapamiętywać rozwiązania przypadków elementarnych i rozwiązania podproblemów, które zostaną obliczone na ich podstawie. inicjacja: wpisz do tablicy wartości, które odpowiadają przypadkom elementarnym progresja: na podstawie wartości wpisanych do tablicy używając formuly rekurencyjnej oblicz rozwiązanie problemu wyższego rzędu i wpisz je do tablicy. Postępuj w ten sposób aż do uzyskania pożądanej wartości. fib(4) fib(2) fib(0) fib(1) fib(0) fib(1) fib(1) fib(2) fib(3)

Transcript of Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego...

Page 1: Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2 Elementy tego ci ągu stanowi

Programowanie dynamiczne

Ciąg Fibonacciego

fib(0)=1

fib(1)=1

fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2

Elementy tego ciągu stanowią liczby naturalne tworzące ciąg o takiej własności, że kolejny wyraz (z wyjątkiem dwóch pierwszych) jest sumą dwóch poprzednich (tj. 1,1,2,3,5, 8,13, …).

Obliczanie czwartego elementu ciągu Fibonacciego

Każde pogrubione wyrażenie stanowi problem elementarny. Problem o rozmiarze n ≥ 2 zostaje rozbity na dwa problemy o mniejszym stopniu skomplikowania: n-1 i n-2. Proces dekompozycji zatrzymuje się na przypadkach elementarnych.

Zaimplementuj powyższy algorytm.

Wada

Można dostrzec, że znaczna część obliczeń jest wykonywana więcej niż jeden raz (np. cała gałęź zaczynająca się od fib(2) jest wręcz zdublowana).

Z powyższą wadą radzi sobie programowanie dynamiczne.

� koncepcja: dla danego problemu stwórz rekurencyjny model jego rozwiązania (wraz z jednoznacznym określeniem przypadków elementarnych). Stwórz tablicę w której będzie można zapamiętywać rozwiązania przypadków elementarnych i rozwiązania podproblemów, które zostaną obliczone na ich podstawie.

� inicjacja: wpisz do tablicy wartości, które odpowiadają przypadkom elementarnym � progresja: na podstawie wartości wpisanych do tablicy używając formuły rekurencyjnej

oblicz rozwiązanie problemu wyższego rzędu i wpisz je do tablicy. Postępuj w ten sposób aż do uzyskania pożądanej wartości.

fib(4)

fib(2)

fib(0) fib(1)

fib(0) fib(1)

fib(1) fib(2)

fib(3)

Page 2: Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2 Elementy tego ci ągu stanowi

Ciąg Fibonacciego przy użyciu programowania dynamicznego

� koncepcja: wzór rekurencyjny jest znany, więc pozostaje zadeklarować tablicę fib[n] do przechowywania obliczanych wartości.

� inicjacja: początkowymi wartościami w tablicy fib będą oczywiście warunki

początkowe: fib[0]=1 oraz fib[1]=1 � progresja: ten punkt zależy ściśle od wzoru rekurencyjnego, który implementujemy za

pomocą tablicy. W naszym przypadku wartością fib[i] w tablicy (dla 2 ≤ i) jest

suma dwóch poprzednio obliczonych wartości: fib[i-1] oraz fib[i-2].

Zaimplementuj powyższy algorytm.

Współczynnik dwumianowy Newtona

���� = �!�!���! dla 0≤k≤n

Dla n i k, które nie są małe nie możemy obliczyć współczynnika dwumianowego

bezpośrednio z tej definicji, ponieważ wartość n! jest bardzo duża nawet dla średnich

wartości n.

Możemy zastosować poniższy wzór rekurencyjny

���� = � �� − 1� − 1� + �� − 1� �1 ��� � = 0 ��� � = �

� ��� 0 < � < �

Napisz program obliczania współczynnika dwumianowego przy użyciu podejścia typu dziel i zwyciężaj.

Algorytm ten jest bardzo niewydajny, oblicza 2���� − 1 wyrazów w celu obliczenia wartości

����. Problem polega na tym, że te same realizacje są rozwiązywane w każdym wywołaniu

rekurencyjnym. Przykładowo wywołania dwumianowyn-1,k-1� oraz dwumianowyn-1,k� wiążą się z obliczeniem wartości dwumianowyn-2,k-1� i realizacja ta jest rozwiązywana niezależnie w każdym wywołaniu. Podejście typu „dziel i zwyciężaj” nigdy nie jest wydajne, kiedy realizacja jest dzielona na dwie mniejsze realizacje, których rozmiar jest zbliżony do rozmiaru oryginalnej realizacji.

Napisz program obliczający współczynnik dwumianowy wykorzystując programowanie dynamiczne.

Znamy już właściwość rekurencyjną, więc wykorzystajmy ją do skonstruowania rozwiązania

wykorzystującego tablicę B, gdzie B[i][j] będzie zawierać wartość "#$% .

Page 3: Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2 Elementy tego ci ągu stanowi

Etapy tworzenia algorytmu:

1. Określamy właściwość rekurencyjną. Zapisujemy ją w kontekście użycia tablicy B

B[i][j]=+,[# − 1][$ − 1] + ,[# − 1][$]���0 < $ < #1���$ = 0���$ = #

� 2. Rozwiązujemy realizację problemu w porządku wstępującym , rozpoczynając od

pierwszego wiersza i wyliczając po kolei wartości w wierszach tablicy B.

Obliczmy wartość B[4][2]=�42� Obliczamy wiersz 0: Etap ten jest wykonywany wyłącznie w celu dokładnego odwzorowania algorytmu. Wartość B[0][0] nie jest potrzebna w dalszych obliczeniach. Obliczamy wiersz 1: B[1][0]=1 B[1][1]=1Obliczamy wiersz 2: B[2][0]=1 B[2][1]=B[1][0]+B[1][1]=1+1=2 B[2][2]=1Obliczamy wiersz 3: B[3][0]=1 B[3][1]=B[2][0]+B[2][1]=1+2=3 B[3][2]=B[2][1]+B[2][2]=2+1=3Obliczamy wiersz 4 B[4][0]=1 B[4][1]=B[3][0]+B[3][1]=1+3=4 B[4][2]=B[3][1]+B[3][2]=3+3=6

Page 4: Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2 Elementy tego ci ągu stanowi

W przykładzie tym obliczaliśmy po kolei coraz większe wartości współczynnika dwumianowego. W każdym przebiegu wartości potrzebne do wykonania bieżących działań były już obliczane i zachowane.

Łańcuchowe mnożenie macierzy

Rozważmy obliczenie iloczynu n macierzy M = M1 • M2 • ... • Mn, gdzie każde Mi jest macierzą mającą

ri-1 wierszy i ri kolumn. Bez względu na to jaki algorytm do macierzy stosujemy istotny wpływ na

całkowitą liczbę operacji potrzebnych do policzenia M ma kolejność mnożenia macierzy.

Przykład

Przyjmujemy, że mnożenie macierzy rozmiaru p x q przez macierz rozmiaru q x r wymaga p• q• r operacji. Chcemy obliczyć iloczyn M = M1•M2•M3•M4 o wymiarach odpowiednio [10 x 20], [20 x 50], [50

x 1], [1 x 100], Obliczmy M wg schematu M = M 1 • (M2• (M3•M 4 ) )

M34 = M3 •M4 = 50 •1 • 100 = 5000 - koszt mnożenia, wymiar powstałej macierzy M34 [50 x 100]

M234 = M2 • M34= 20•50 • 100 = 100000- koszt mnożenia, wymiar macierzyM234 to [20 x 100]

M1 • M234 = 10 • 20 • 100 = 20000 - koszt mnożenia

5000 + 100000 + 20000 = 125000 - całkowity koszt mnożenia

Policzmy teraz M dla schematu

Page 5: Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2 Elementy tego ci ągu stanowi
Page 6: Programowanie dynamiczne - KUL · 2015-05-18 · Programowanie dynamiczne Ci ąg Fibonacciego fib(0)=1 fib(1)=1 fib(n)=fib(n-1)+fib(n-2), gdzie n ≥ 2 Elementy tego ci ągu stanowi

Chcemy otrzymać optymalną kolejność mnożenia na podstawie powyższej tablicy s.

W tym celu najpierw odwiedzamy element s[1][n], ( w naszym przykładzie n=4). Więc

s[1][4]=3, co oznacza, że optymalna kolejność dla mnożenia macierzy od 34do 35posiada rozkład

(343637)35

(s[1][4] czyli 3 jest punktem, w którym macierze powinny zostać rozdzielone w celu otrzymania czynników).

Następnie chcemy określić jak wygląda rozkład dla mnożenia macierzy od 34 do 37. Zatem sprawdzamy ile wynosi s[1][3]. Widzimy, że s[1][3]=1. Więc rozkład od 34 do 37 przybiera następującą postać 34(3637).

Zatem wiemy, że poprawna kolejność nawiasów jest następująca ((34(3637))35.

Zaimplementuj powyższy algorytm łańcuchowego mnożenia macierzy.