programowanie dynamiczne


Ćwiczenie numer: 6 Data oddania ćwiczenia:

Temat: Programowanie dynamiczne

1. Treść zadania.

Znajomość metod programowania dynamicznego oraz zakresu jej stosowania. Znajomość problemu znajdowania najdłuższego wspólnego podciągu.

Przebieg ćwiczenia.

a) Dla obranych z ciągu symboli zbioru liter alfabetu łacińskiego, wyznaczyć najdłuższy wspólny podciąg LWP. Podciągiem nazywamy ciąg, który otrzymuje się z ciągu oryginalnego prze usunięcie z niego niektórych lub żadnego elementu. Wspólnym podciągiem nazywamy podciąg występujący w obu ciągach wejściowych.

Rozwiązać dwoma metodami:

- algorytmem przeszukiwania wyczerpującego przestrzeni rozwiązań (generacja wszystkich podciągów X i sprawdzanie, czy jest ono również podciągiem Y i zapamiętywanie najdłuższego znalezionego podciągu).

- metody programowania dynamicznego.

b) Opisz ogólnie działanie algorytmu na idei programowania dynamicznego ilustrując go przykładem znajdowania NWP(X,Y)

c) Zbadaj zależność czasu obliczeń metod od długości ciągów wejściowych. Można założyć równą długość obu ciągów. T=f(n) przedstawić ma odrębnych wykresach dla poszczególnych algorytmów i na wykresie wspólnym

d) Sformować wnioski z przeprowadzonych badań :

do jakiej klasy problemów należą zagadnienia znajdowania najdłuższego wspólnego podciągu. Jaka jest zależność obliczeń obu metod i jakie ma konsekwencje dla efektywności ich działań. Do jakiego typu problemu możliwe jest zastosowanie programowania dynamicznego.

2. Znajdowanie najdłuższego wspólnego podciągu metodą wyczerpującą.

Algorytm wyszukiwania najdłuższego wspólnego podciągu polega na generacji wszystkich możliwych podciągów ciągu X i sprawdzaniu dla każdego z nich, czy jest też podciągiem ciągu Y, zapamiętując najdłuższy wspólny podciąg dotychczas znaleziony. Każdy podciąg ciągu X odpowiada jednoznacznie podzbiorowi (1,2,..,n) indeksów ciągu X. Istnieje zatem 2n podciągów X, co przy dużej ilości elementów w ciągu powoduje, że algorytm ten staje się bezużyteczny. Złożoność tego algorytmu jest wykładnicza O(2n).

Przykład:

X=<A,B,C>

Y=<W,A,C>

Wszystkie możliwe kombinacje wyglądają następująco:

Zbiór pusty; C; B; CB; A; AC; AB; ABC

Jak można zauważyć pasuje kilka kombinacji w ciągu Y, które spełniają warunek podciągu ciągu X jednak tą najdłuższą jest ciąg AC, który metoda wybierze i przedstawi jako wynik.

3. Znajdowanie najdłuższego wspólnego podciągu metodą programowania dynamicznego.

Wstęp teoretyczny.

Metoda programowania dynamicznego opiera się na tzw. "Zasadzie tymalnści" . Optymalny ciąg decyzji ma taką własność, że niezależnie od stanu początkowego i początkowych decyzji, następne decyzje muszą tworzyć optymalny ciąg decyzji względem stanu zastanego.

Opis działania algorytmu.

Procedura ciag_dynamicznie dla danych ciągów X=<x1,x2,..xm> i Y=<y1,y2,..yn> oblicza wartości c[i,j] i zapamiętuje je w tablicy c[0..m,0..n]. Obliczenia te są wykonywane wzdłuż wierszy, a w każdym wierszu od strony lewej do prawej. Tworzona jest także tablica b[1..m,1,..n], która ułatwia później konstrukcję optymalnego rozwiązania. Intuicyjnie, wartość b[i,j] wskazuje na pole w tablicy c, odpowiadające wybranemu podczas obliczania c[i,j] optymalnemu rozwiązaniu podproblemu. Procedura ciąg_dynamicznie oblicza tablice b i c; pole c[m,n] zawiera długość NWP ciągów X i Y. Czas działania tej procedury wynosi O(nm), ponieważ obliczone wartości każdego z pól tablicy c i b zajmuje O(1) jednostek czasu.

procedure ciag_dynamicznie;

var

m,n,i,j:integer;

begin

m:=nx;

n:=ny;

for i:=1 to m do

c[i,0]:=0;

for j:=0 to n do

c[0,j]:=0;

for i:=1 to m do

for j:=1 to n do

if x[i]=y[j] then

begin

c[i,j]:=c[i-1,j-1]+1;

b[i,j]:=skos;

end

else

begin

if c[i-1,j]>=c[i,j-1] then

begin

c[i,j]:=c[i-1,j];

b[i,j]:=gora;

end

else

begin

c[i,j]:=c[i,j-1];

b[i,j]:=lewo;

end;

end;

end;

Przykład

X=<A,B,C,D,E>

0x08 graphic
Y=<B,A,D,C,E>

Strzałki w tej tablicy oznaczają zawartość tablicy b, wartości liczbowe oznaczają wartości zapisane w tabeli c . Prawa dolna komórka w tabeli informuje o długości podciągu a komórki zaznaczone kolorem oznaczają drogę wskazywaną przez strzałki od tego pola. Strzałki ukośne w polach na tej drodze wskazują elementy składające się na podciąg.

4. Kod programu.


program ciagi;

uses

dos,crt;

const

max_y=5;

max_x=5;

krok=5;

wyswietlanie=true;

type

strzalki=(lewo,gora,skos);

var

c:array[0..max_x,0..max_y] of integer;

b:array[0..max_x,0..max_y]of strzalki;

x:array[1..max_x]of char;

y:array[1..max_y]of char;

xx:array [1..max_x] of byte;

yy:array [1..max_y] of byte;

j,i,nx,ny:integer;

stan:strzalki;

czas_w,czas_d,poczatek,koniec:real;

f:text;

Function WlaczTimer :LongInt;

Var Reg :Registers;

Znacznik :Byte;

Begin

Znacznik:=0;

Reg.AH:=$83;

Reg.AL:=0;

Reg.CX:=$7FFF;

Reg.DX:=$FFFF;

Reg.BX:=Ofs(Znacznik);

Reg.ES:=Seg(Znacznik);

Intr($15,Reg);

WlaczTimer:=$7FFFFFFF;

End;

Function WylaczTimer :LongInt;

Var Reg :Registers;

Begin

WylaczTimer:=MemL[$0040:$009C];

Reg.AH:=$83;

Reg.AL:=1;

Intr($15,Reg);

End;

Function CzasWykonania :Real;

Var Czas :Real;

Begin

Czas:=(Poczatek-koniec);

CzasWykonania:=Czas;

End;

procedure losuj_ciagi;

var

i:integer;

begin

for i:=1 to max_x do

x[i]:=chr(random(25)+65);

for i:=1 to max_y do

y[i]:=chr(random(25)+65);

end;

procedure przelicz_x(liczba:integer);

var

i,j:integer;

begin

for i:=1 to nx do

begin

if (liczba mod 2)=0 then xx[i]:=0

else xx[i]:=1;

liczba:=liczba div 2;

end;

end;

procedure wyczerpujaco;

var

p:char;

i,j,k,max,licznik:integer;

m,maska,max_maska:longint;

label 1,2;

begin

max:=0;

maska:=1;

for i:=1 to nx do maska:=maska*2;

while maska>1 do

begin

maska:=maska-1;

m:=maska;

k:=1;

licznik:=0;

for j:=1 to nx do

begin

if m mod 2=1 then

begin

while k<=ny do

begin

if(y[k]=x[j]) then

begin

k:=k+1;

licznik:=licznik+1;

goto 1; {znaleziony pasujacy znak}

end;

k:=k+1;

end;

goto 2; {nia ma tego znaku w tablicy Y}

end; {ta maska jest zla}

1:

m:=m div 2;

end;

{ok. ten wzorzec pasuje}

if max<licznik then

begin

max:=licznik;

max_maska:=maska;

end;

2:

end;

if wyswietlanie then

begin

write('wyczerpujace: ');

if max>0 then

begin

przelicz_x(max_maska);

for j:=1 to nx do

begin

if xx[j]=1 then write(x[j],' ');

m:=m div 2;

end;

if wyswietlanie then writeln;

end

end;

end;

procedure pokaz;

var

i:integer;

begin

writeln('ciag x');

for i:=1 to nx do write(' ',x[i]);

writeln;

writeln('ciag y');

for i:=1 to ny do write(' ',y[i]);

end;

procedure ciag_dynamicznie;

var

m,n,i,j:integer;

begin

m:=nx;

n:=ny;

for i:=1 to m do

c[i,0]:=0;

for j:=0 to n do

c[0,j]:=0;

for i:=1 to m do

for j:=1 to n do

if x[i]=y[j] then

begin

c[i,j]:=c[i-1,j-1]+1;

b[i,j]:=skos;

end

else

begin

if c[i-1,j]>=c[i,j-1] then

begin

c[i,j]:=c[i-1,j];

b[i,j]:=gora;

end

else

begin

c[i,j]:=c[i,j-1];

b[i,j]:=lewo;

end;

end;

end;

procedure pisz_ciag(i,j:integer);

begin

if (i=0) or (j=0) then exit;

if b[i,j]=skos then

begin

pisz_ciag(i-1,j-1);

write(X[i]);

end

else

if b[i,j]=gora then

pisz_ciag(i-1,j)

else

pisz_ciag(i,j-1)

end;

procedure test;

begin

x[1]:='A';x[2]:='B';x[3]:='C';x[4]:='D';x[5]:='E';

y[1]:='B';y[2]:='A';Y[3]:='D';Y[4]:='C';Y[5]:='E';

end;

begin

clrscr;

randomize;

ny:=0;

nx:=0;

poczatek:= WlaczTimer;

koniec:=wylaczTimer;

Assign(f,'ciagi.txt');

rewrite(f);

writeln('ile znakow | wyczerpujaco | dynamicznie');

writeln(f,'ile znakow | wyczerpujaco | dynamicznie');

repeat

czas_w:=0;

czas_d:=0;

nx:=nx+krok;

ny:=nx;

for j:=1 to 10 do

begin

losuj_ciagi;

{ TEST;}

poczatek:= WlaczTimer;

ciag_dynamicznie;

koniec:=wylaczTimer;

czas_d:=czas_d+czaswykonania;

if wyswietlanie then

begin

pokaz;

writeln;

writeln('dynamicznie') ;

pisz_ciag(nx,ny);

writeln;

writeln('metoda wyczerpujaca');

end;

poczatek:= WlaczTimer;

wyczerpujaco;

koniec:=wylaczTimer;

czas_w:=czas_w+czaswykonania;

end;

writeln(nx:8,czas_w:15:0,czas_d:15:0);

writeln(f,nx:8,czas_w:15:0,czas_d:15:0);

until max_x<nx+krok;

close(f);

writeln(' ********KONIEC********');

writeln;

sound(1000);

delay(100);

nosound;

readln;

end.


5. Tabela pomiarów.

Czas = sumie 10 pomiarów

ile znakow

wyczerpujaco

dynamicznie

[-]

[s]

[s]

1

0.000976

0.000976

2

0.000976

0.000976

3

0.000976

0.000976

4

0.000976

0.000976

5

0.001952

0.000976

6

0.001952

0.001952

7

0.003904

0.000976

8

0.004880

0.002928

9

0.014640

0.001952

10

0.031232

0.001952

11

0.064416

0.003904

12

0.137616

0.003904

13

0.313296

0.002928

14

0.573888

0.000976

15

1.117520

0.001952

16

2.283840

0.002928

17

5.468528

0.001952

18

10.674512

0.002928

19

22.449952

0.003904

20

41.101312

0.003904

21

107.866544

0.002928

6. Wykresy.

0x08 graphic

0x08 graphic

0x08 graphic

7. Wnioski.

Ze względu na niedokładność funkcji obliczających czas wykonywania się algorytmów zastosowałem w swoich pomiarach sumaryczny czas 10 kolejnych wywołań algorytmu dla tej samej ilości znaków.

Zacznę od omówienia metody wyczerpującej. Jest to metoda bardzo nieefektywna, gdyż czas wykonywania się algorytmu bardzo mocno wzrasta wraz z ilością znaków w ciągu. I tak np. dla 10 znaków algorytm wykonuje się 0,3 ms a dla 20 znaków aż 10 s. Dzieje się to dlatego, że algorytm ten sprawdza wszystkie możliwe kombinacje, których jest 2n (n - liczba znaków). Przykładowo dla 4 znaków jest 16 kombinacji, które można zapisać jako liczba binarna od 1111 do 0000 za każdym zmniejszając jej wartość o 1 czyli w każdej kolejnej iteracji pętli głównej algorytmu odejmujemy 1 od maski np. 1111 > 1110 > 1101 itd. . Łatwo zauważyć, że złożoność obliczeniowa tego algorytmu jest wykładnicza i wynosi O(2n) co w praktyce dyskwalifikuje ten algorytm do jakichkolwiek zastosowań praktycznych oprócz demonstracji poprzez porównanie z tą metoda innych metod np. metody programowania dynamicznego.

Przeciwieństwem metody wyczerpującej jest metoda dynamiczna, która jest bardzo szybka i efektywna. Jej złożoność obliczeniowa jest wykładnicza i wynosi O(m*n) a przy dwóch jednakowej długości ciągach O(n2) . Jej wykładniczy charakter w tym przypadku nie jest wadą ponieważ w porównaniu z bardzo stromą charakterystyką przy metodzie wyczerpującej wypada ona fenomenalnie. Porównajmy dwa przypadki czas wykonania algorytmu NWP dla metody wyczerpującej przy długości ciągu równej 20 wynosi 220 jednostek czasu czyli 1048576 jednostek. Dla porównania metodą programowania dynamicznego przy równych co do długości ciągach X i Y czas wykonania jest równy 202 jednostek czasu czyli 400 . Wniosek dla tego przypadku programowanie dynamiczne jest 2600 razy szybsze i wraz ze wzrostem długości ciągu będzie ta dysproporcja rosnąć. Na wykresach to można bardzo dobrze zaobserwować, jednak ze względu na duże różnice na wspólnym wykresie zastosowałem skalę logarytmiczną.

Zastosowanie programowania dynamicznego.

W algorytmie opartym na programowaniu dynamicznym rozwiązuje się każdy problem tylko raz, w każdym kroku wybierając najbardziej optymalne rozwiązanie. Następnie zapamiętuje się wynik w odpowiedniej tabeli , unikając w ten sposób wielokrotnych obliczeń dla tego samego podproblemu.

Programowanie to ma zastosowanie wszędzie tam, gdzie mamy do czynienia z problemami optymalizacyjnymi. W tego typu zagadnieniach możliwych jest wiele rozwiązań. Za każdym razem rozwiązanie jest pewną liczbą. Rozwiązanie optymalne to takie, które za każdym razem ma optymalny (min. max.) koszt.

1

5

0x01 graphic

0x01 graphic

0x01 graphic

0x01 graphic



Wyszukiwarka

Podobne podstrony:
Metody układania algorytmów rekurencja, metoda dziel i zwyciężaj, programowanie dynamiczne, metoda
Programowanie dynamiczne
Programowanie dynamiczne (2)
Badania Operacyjne UW, wykład 3 produkcja-zapasy, Programowanie dynamiczne
Programowanie Dynamiczne 2
Metoda programowania dynamicznego
Programowanie Dynamiczne
8 Programowanie dynamiczne
programowanie dynamiczne id 396 Nieznany
Algorytmy wyklady, Programowanie dynamiczne, MATRIX-CHAIN-ORDER ( p );
Wykład nr 5 Optymalizacja (programowania dynamicznego)
Metody układania algorytmów rekurencja, metoda dziel i zwyciężaj, programowanie dynamiczne, metoda
Programowanie dynamiczne

więcej podobnych podstron