You are on page 1of 50

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Podstawy algorytmw
z przykadami w C++
Autorzy: Richard Neapolitan, Kumarss Naimipour
Tumaczenie: Bartomiej Garbacz, Pawe Gonera,
Artur Szczepaniak, Mikoaj Szczepaniak
ISBN: 83-7361-429-X
Tytu oryginau: Foundations of Algorithms.
Using C++ Pseudocode
Format: B5, stron: 648
Algorytmy s jednym z fundamentw programowania. Prawidowo zaprojektowany
algorytm jest podstaw efektywnego i niezawodnego programu. Opisanie problemu
w postaci algorytmu nie jest prostym zadaniem wymaga wiedzy z zakresu
matematyki, umiejtnoci oceny zoonoci obliczeniowej i znajomoci zasad
optymalizacji oblicze. Istnieje wiele metod projektowania algorytmw.
Znajomo tych metod znacznie uatwia analiz zagadnienia i przedstawienie go
w postaci zalgorytmizowanej.
Ksika Podstawy algorytmw z przykadami w C++ to kompletny podrcznik
powicony tym wanie zagadnieniom. Przedstawia sposoby podejcia
do rozwizywania zagadnie projektowych, udowadnia, e sporo z nich mona
zrealizowa rnymi metodami, a take uczy, jak dobra waciw metod
do postawionego problemu. Materia podzielony jest na wykady, zilustrowane
pseudokodem przypominajcym jzyk C++, co bardzo uatwia zastosowanie
poznanej wiedzy w praktyce.
Wprowadzenie do projektowania algorytmw
Zastosowanie techniki dziel i zwyciaj
Algorytmy programowania dynamicznego
Analiza zoonoci obliczeniowej algorytmw na przykadzie
lgorytmw sortowania i przeszukiwania
Algorytmy z zakresu teorii liczb
Algorytmy kompresji danych i kryptografii
Programowanie rwnolege

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Wykady powicone algorytmom s uzupenione dodatkami, zawierajcymi


kompendium niezbdnej wiedzy z dziedziny matematyki, technik rekurencyjnych
i algebry zbiorw.
Podstawy algorytmw z przykadami w C++ to doskonay podrcznik dla uczniw,
studentw i wszystkich, ktrzy chc pozna t dziedzin wiedzy.

Spis treci
O Autorach ..............................................................................................9
Przedmowa............................................................................................11
Rozdzia 1. Algorytmy wydajno, analiza i rzd ...................................................17
1.1. Algorytmy......................................................................................................................... 18
1.2. Znaczenie opracowywania wydajnych algorytmw......................................................... 25
1.2.1. Wyszukiwanie sekwencyjne a wyszukiwanie binarne ........................................... 26
1.2.2. Cig Fibonacciego .................................................................................................. 28
1.3. Analiza algorytmw.......................................................................................................... 33
1.3.1. Analiza zoonoci.................................................................................................. 33
1.3.2. Zastosowanie teorii................................................................................................. 41
1.3.3. Analiza poprawnoci .............................................................................................. 42
1.4. Rzd .................................................................................................................................. 42
1.4.1. Intuicyjne wprowadzenie do problematyki rzdu................................................... 43
1.4.2. Formalne wprowadzenie do problematyki rzdu ................................................... 45
1.4.3. Wykorzystanie granic do okrelenia rzdu............................................................. 56
1.5. Zarys dalszej treci ksiki ............................................................................................... 59
wiczenia ................................................................................................................................. 60

Rozdzia 2. Dziel i zwyciaj.....................................................................................65


2.1. Wyszukiwanie binarne ...................................................................................................... 66
2.2. Sortowanie przez scalanie ................................................................................................. 71
2.3. Podejcie typu dziel i zwyciaj........................................................................................ 77
2.4. Sortowanie szybkie (sortowanie przez podstawienie podziaw)..................................... 78
2.5. Algorytm Strassena mnoenia macierzy ........................................................................... 85
2.6. Arytmetyka wielkich liczb cakowitych............................................................................ 90
2.6.1. Reprezentacja wielkich liczb cakowitych: dodawanie i inne operacje
wykonywane w czasie liniowym ......................................................................... 91
2.6.2. Mnoenie wielkich liczb cakowitych .................................................................... 91
2.7. Okrelanie wartoci progowych ........................................................................................ 97
2.8. Przeciwwskazania do uywania podejcia dziel i zwyciaj .......................................... 101
wiczenia ............................................................................................................................... 103

Rozdzia 3. Programowanie dynamiczne .................................................................111


3.1. Wspczynnik dwumianowy ........................................................................................... 112
3.2. Algorytm Floyda okrelania najkrtszej drogi w grafie.................................................. 117
3.3. Programowanie dynamiczne a problemy optymalizacyjne............................................. 125
3.4. acuchowe mnoenie macierzy .................................................................................... 127
3.5. Optymalne drzewa wyszukiwania binarnego.................................................................. 136
3.6. Problem komiwojaera.................................................................................................... 145
wiczenia ............................................................................................................................... 152

Podstawy algorytmw z przykadami w C++

Rozdzia 4. Podejcie zachanne ............................................................................157


4.1. Minimalne drzewo rozpinajce....................................................................................... 161
4.1.1. Algorytm Prima .................................................................................................... 163
4.1.2. Algorytm Kruskala ............................................................................................... 169
4.1.3. Porwnanie algorytmu Prima z algorytmem Kruskala......................................... 173
4.1.4. Dyskusja kocowa ................................................................................................ 173
4.2. Algorytm Dijkstry najkrtszych drg z jednego rda ................................................. 174
4.3. Szeregowanie.................................................................................................................. 177
4.3.1. Minimalizacja cakowitego czasu w systemie ...................................................... 178
4.3.2. Szeregowanie z terminami granicznymi............................................................... 180
4.4. Kod Huffmana ................................................................................................................ 188
4.4.1. Kody prefiksowe................................................................................................... 189
4.4.2. Algorytm Huffmana.............................................................................................. 190
4.5. Podejcie zachanne a programowanie dynamiczne: problem plecakowy ..................... 194
4.5.1. Podejcie zachanne do problemu plecakowego 0-1 ............................................ 195
4.5.2. Podejcie zachanne do uamkowego problemu plecakowego ............................. 196
4.5.3. Podejcie programowania dynamicznego do problemu plecakowego 0-1........... 197
4.5.4. Poprawiona wersja algorytmu programowania dynamicznego
dla problemu plecakowego 0-1 .......................................................................... 197
wiczenia ............................................................................................................................... 201

Rozdzia 5. Algorytmy z powrotami.........................................................................207


5.1. Techniki algorytmw z powrotami................................................................................. 208
5.2. Problem n-krlowych ..................................................................................................... 215
5.3. Zastosowanie algorytmu Monte Carlo do oszacowania wydajnoci
algorytmu z powrotami ................................................................................................ 219
5.4. Problem sumy podzbiorw ............................................................................................. 223
5.5. Kolorowanie grafu .......................................................................................................... 228
5.6. Problem cyklu Hamiltona ............................................................................................... 232
5.7. Problem plecakowy 0-1 .................................................................................................. 235
5.7.1. Algorytm z powrotami dla problemu plecakowego 0-1 ....................................... 235
5.7.2. Porwnanie algorytmu programowania dynamicznego
z algorytmem z powrotami do rozwizywania problemu plecakowego 0-1......... 244
wiczenia ............................................................................................................................... 245

Rozdzia 6. Metoda podziau i ogranicze ...............................................................251


6.1. Ilustracja metody podziau i ogranicze dla problemu plecakowego 0-1 ...................... 253
6.1.1. Przeszukiwanie wszerz z przycinaniem metod podziau i ogranicze ............... 253
6.1.2. Przeszukiwanie typu najpierw najlepszy z przycinaniem
metod podziau i ogranicze............................................................................. 258
6.2. Problem komiwojaera ................................................................................................... 264
6.3. Wnioskowanie abdukcyjne (diagnozowanie) ................................................................. 272
wiczenia ............................................................................................................................... 281

Rozdzia 7. Wprowadzenie do zoonoci obliczeniowej: problem sortowania ............285


7.1. Zoono obliczeniowa ................................................................................................. 286
7.2. Sortowanie przez wstawianie i sortowanie przez wybieranie ........................................ 288
7.3. Dolne ograniczenia dla algorytmw usuwajcych co najwyej jedn inwersj
dla jednej operacji porwnania .................................................................................... 294
7.4. Przypomnienie algorytmu sortowania przez scalanie..................................................... 297
7.5. Przypomnienie algorytmu szybkiego sortowania........................................................... 303
7.6. Sortowanie stogowe........................................................................................................ 305
7.6.1. Stogi i podstawowe na nich operacje.................................................................... 305
7.6.2. Implementacja algorytmu sortowania stogowego ................................................ 309

Spis treci

7.7. Zestawienie algorytmw sortowania przez scalanie, sortowania szybkiego


i sortowania stogowego................................................................................................ 316
7.8. Dolne ograniczenia dla algorytmw sortujcych wycznie na podstawie
operacji porwnania kluczy ......................................................................................... 317
7.8.1. Drzewa decyzyjne dla algorytmw sortujcych ................................................... 317
7.8.2. Dolne ograniczenia dla najgorszego przypadku ................................................... 320
7.8.3. Dolne ograniczenia dla redniego przypadku....................................................... 323
7.9. Sortowanie przez podzia (sortowanie pozycyjne) ......................................................... 328
wiczenia ............................................................................................................................... 332

Rozdzia 8. Wicej o zoonoci obliczeniowej: problem przeszukiwania ...................339


8.1. Dolne ograniczenia dla przeszukiwania wycznie na podstawie
porwnywania wartoci kluczy.................................................................................... 340
8.1.1. Dolne ograniczenia dla najgorszego przypadku ................................................... 343
8.1.2. Dolne ograniczenia dla redniego przypadku....................................................... 345
8.2. Przeszukiwanie przez interpolacj.................................................................................. 351
8.3. Przeszukiwanie w drzewach ........................................................................................... 354
8.3.1. Drzewa wyszukiwania binarnego ......................................................................... 355
8.3.2. B-drzewa............................................................................................................... 360
8.4. Mieszanie........................................................................................................................ 361
8.5. Problem wyboru: wprowadzenie metody dyskusji z adwersarzem ................................ 367
8.5.1. Znajdowanie najwikszego klucza ....................................................................... 367
8.5.2. Znajdowanie zarwno najmniejszego, jak i najwikszego klucza ....................... 369
8.5.3. Znajdowanie drugiego najwikszego klucza ........................................................ 376
8.5.4. Znajdowanie k-tego najmniejszego klucza........................................................... 381
8.5.5. Algorytm probabilistyczny dla problemu wyboru................................................ 390
wiczenia ............................................................................................................................... 395

Rozdzia 9. Zoono obliczeniowa i trudno problemw:


wprowadzenie do teorii o zbiorze NP .....................................................401
9.1. Trudno ......................................................................................................................... 402
9.2. Ponowne omwienie zagadnienia rozmiaru danych wejciowych................................. 404
9.3. Trzy gwne kategorie problemw ................................................................................. 409
9.3.1. Problemy, dla ktrych wynaleziono algorytmy wielomianowe ........................... 409
9.3.2. Problemy, ktrych trudno zostaa udowodniona............................................... 410
9.3.3. Problemy, ktrych trudno nie zostaa udowodniona, jednak nie udao si
znale rozwizujcych je algorytmw wielomianowych................................. 411
9.4. Teoria o zbiorze NP ........................................................................................................ 411
9.4.1. Zbiory P i NP ........................................................................................................ 414
9.4.2. Problemy NP-zupene........................................................................................... 419
9.4.3. Problemy NP-trudne, NP-atwe i NP-rwnowane .............................................. 431
9.5. Sposoby rozwizywania problemw NP-trudnych ........................................................ 435
9.5.1. Algorytm przybliony dla problemu komiwojaera............................................. 436
9.5.2. Przybliony algorytm dla problemu pakowania ................................................... 441
wiczenia ............................................................................................................................... 446

Rozdzia 10. Algorytmy teorii liczb ...........................................................................449


10.1. Przegld teorii liczb....................................................................................................... 450
10.1.1. Liczby zoone i liczby pierwsze ...................................................................... 450
10.1.2. Najwikszy wsplny dzielnik............................................................................ 451
10.1.3. Rozkadanie liczb cakowitych na czynniki pierwsze ....................................... 455
10.1.4. Najmniejsza wsplna wielokrotno ................................................................. 457
10.2. Wyznaczanie najwikszego wsplnego dzielnika......................................................... 457
10.2.1. Algorytm Euklidesa........................................................................................... 458
10.2.2. Rozszerzenie algorytmu Euklidesa.................................................................... 462

Podstawy algorytmw z przykadami w C++

10.3. Przegld arytmetyki modularnej.................................................................................... 465


10.3.1. Teoria grup ........................................................................................................ 465
10.3.2. Kongruencja modulo n ...................................................................................... 467
10.3.3. Podgrupy ........................................................................................................... 473
10.4. Rozwizywanie modularnych rwna liniowych ......................................................... 479
10.5. Obliczanie modularnych potg...................................................................................... 485
10.6. Znajdowanie wielkich liczb pierwszych ....................................................................... 488
10.6.1. Szukanie liczby pierwszej ................................................................................. 488
10.6.2. Sprawdzanie, czy dana liczba cakowita jest liczb pierwsz........................... 489
10.7. System szyfrowania RSA z publicznym kluczem......................................................... 508
10.7.1. Systemy szyfrowania z kluczem publicznym.................................................... 508
10.7.2. System szyfrowania RSA .................................................................................. 509
wiczenia ............................................................................................................................... 512

Rozdzia 11. Wprowadzenie do algorytmw rwnolegych..........................................517


11.1. Architektury rwnolege................................................................................................ 521
11.1.1. Mechanizm sterowania...................................................................................... 521
11.1.2. Organizacja przestrzeni adresowej .................................................................... 522
11.1.3. Sieci czce ...................................................................................................... 524
11.2. Model PRAM ................................................................................................................ 527
11.2.1. Projektowanie algorytmw dla modelu CREW PRAM.................................... 531
11.2.2. Projektowanie algorytmw dla modelu CRCW PRAM.................................... 538
wiczenia ............................................................................................................................... 541

Dodatek A Przegld niezbdnej wiedzy matematycznej...........................................543


A.1. Notacja............................................................................................................................ 543
A.2. Funkcje ........................................................................................................................... 545
A.3. Indukcja matematyczna .................................................................................................. 546
A.4. Twierdzenia i lematy ...................................................................................................... 553
A.5. Logarytmy....................................................................................................................... 554
A.5.1. Definicja i waciwoci logarytmw.................................................................... 554
A.5.2. Logarytm naturalny.............................................................................................. 557
A.6. Zbiory ............................................................................................................................. 559
A.7. Permutacje i kombinacje................................................................................................. 560
A.8. Prawdopodobiestwo...................................................................................................... 563
A.8.1. Losowo ............................................................................................................. 569
A.8.2. Warto oczekiwana ............................................................................................ 573
wiczenia ............................................................................................................................... 575

Dodatek B Rozwizywanie rwna rekurencyjnych


na potrzeby analizy algorytmw rekurencyjnych ....................................581
B.1. Rozwizywanie rekurencji za pomoc indukcji ............................................................. 581
B.2. Rozwizywanie rekurencji z zastosowaniem rwna charakterystycznych................... 585
B.2.1. Homogeniczna rekurencja liniowa....................................................................... 585
B.2.2. Niehomogeniczna rekurencja liniowa.................................................................. 594
B.2.3. Zamiana zmiennej (przeksztacenie dziedziny) ................................................... 600
B.3. Rozwizywanie rekurencji przez podstawianie.............................................................. 603
B.4. Rozszerzanie wynikw dla n bdcego potg dodatniej staej b
do wynikw dla dowolnego n ...................................................................................... 605
B.5. Dowody twierdze.......................................................................................................... 611
wiczenia ............................................................................................................................... 614

Dodatek C Struktury danych dla zbiorw rozcznych .............................................621


Dodatek D Bibliografia ..........................................................................................631

Rozdzia 3.

Programowanie
dynamiczne
Jak zapewne Czytelnik pamita, liczba skadnikw obliczanych przez algorytm
typu dziel i zwyciaj w celu okrelenia n-tego wyrazu cigu Fibonacciego (algorytm 1.6) jest wykadnicza w stosunku do n. Wynika to z faktu, e podejcie typu
dziel i zwyciaj rozwizuje realizacj problemu poprzez jej podzia na mniejsze
realizacje, a nastpnie bezporednie rozwizywanie owych mniejszych realizacji. Jak
stwierdzono w rozdziale 2., jest to podejcie zstpujce. Sprawdza si ono w przypadku problemw w rodzaju sortowania przez scalanie, gdzie mniejsze instancje nie
s ze sob powizane. Dzieje si tak, poniewa kada z nich skada si z tablicy
kluczy, ktre musz zosta posortowane niezalenie. Jednake w przypadku problemw takich, jak n-ty wyraz cigu Fibonacciego, mniejsze instancje s ze sob
powizane. Przykadowo, zgodnie z tym, co przedstawiono w podrozdziale 1.2,
obliczenie pitego wyrazu cigu Fibonacciego wymaga obliczenia wyrazu trzeciego i czwartego. Jednak procesy okrelania czwartego i trzeciego wyrazu s ze
sob zwizane o tyle, e oba wymagaj znajomoci wyrazu drugiego. Ze wzgldu
na fakt, e algorytm typu dziel i zwyciaj wykonuje oba procesy niezalenie, drugi
wyraz cigu Fibonacciego jest obliczany wielokrotnie. W przypadku problemw,

112

Podstawy algorytmw z przykadami w C++

w ktrych mniejsze realizacje s ze sob powizane, czsto okazuje si, e algorytm typu dziel i zwyciaj wielokrotnie rozwizuje te same realizacje i w efekcie
jest on bardzo niewydajny.
Programowanie dynamiczne (ang. dynamic programming), technika omawiana
w niniejszym rozdziale, opiera si na przyjciu odwrotnego podejcia. Programowanie dynamiczne jest podobne do podejcia typu dziel i zwyciaj o tyle, e
realizacja problemu zostaje podzielona na mniejsze realizacje. Jednak tym razem
najpierw s rozwizywane mae realizacje, a ich wyniki zostaj przechowane; pniej, kiedy zajdzie taka potrzeba, algorytm moe si do nich bezporednio odwoywa, zamiast oblicza je ponownie. Pojcie programowanie dynamiczne wywodzi si z teorii sterowania i w tym kontekcie programowanie oznacza uycie
tablicy (tabeli), w ramach ktrej jest konstruowane rozwizanie. Jak wspomniano
w rozdziale 1., wydajny algorytm (algorytm 1.7) obliczania n-tego wyrazu cigu
Fibonacciego jest przykadem programowania dynamicznego. Algorytm ten okrela
n-ty wyraz cigu Fibonacciego poprzez konstruowanie po kolei pierwszych n+1
wyrazw w tablicy f indeksowanej od 0 do n. W przypadku algorytmu programowania dynamicznego konstruujemy rozwizanie od dou tablicy (lub cigu tablic).
Programowanie dynamiczne stanowi wic podejcie wstpujce (ang. bottom-up).
Niekiedy, jak w przypadku algorytmu 1.7, po opracowaniu algorytmu wykorzystujcego tablic (lub cig tablic) istnieje moliwo ulepszenia go, tak aby zwolni
wiele niepotrzebnie przydzielonej przestrzeni pamiciowej.
Opracowanie algorytmu programowania dynamicznego polega na wykonaniu nastpujcych dziaa:
1. Okrelamy waciwo rekurencyjn, ktra pozwala znale rozwizanie

realizacji problemu.
2. Rozwizujemy realizacj problemu zgodnie z podejciem wstpujcym,

najpierw rozwizujc mniejsze realizacje.


W celu zilustrowania tych dziaa w podrozdziale 3.1 przedstawiono kolejny prosty
przykad programowania dynamicznego. W pozostaych podrozdziaach przedstawiono bardziej zaawansowane przykady wykorzystania tego typu programowania.

3.1. Wspczynnik dwumianowy


Wspczynnik dwumianowy (ang. binomial coefficient), ktry omwiono w podrozdziale A.7 w dodatku A, jest pokrelony zalenoci:

n
n!
=
dla 0 k n
k
k
n
k )!
!
(

Dla wartoci n i k, ktre nie s mae, nie moemy obliczy wspczynnika dwumianowego bezporednio z tej definicji, poniewa warto n! jest bardzo dua
nawet dla rednich wartoci n. W wiczeniach zostanie wykazane, e:

Rozdzia 3.

Programowanie dynamiczne

n 1 n 1
n
+

0<k <n
= k 1 k
k

1
k = 0 lub k = n

113

(3.1)

Moemy wyeliminowa konieczno obliczania wyraenia n! lub k!, wykorzystujc waciwo rekurencyjn. Sugeruje to zdefiniowanie poniszego algorytmu typu dziel i zwyciaj.
Algorytm 3.1.

Obliczanie wspczynnika dwumianowego przy uyciu podejcia


typu dziel i zwyciaj
Problem: oblicz wspczynnik dwumianowy.
Dane wejciowe: nieujemne liczby cakowite n i k, gdzie k n.

Dane wyjciowe: bin warto wspczynnika dwumianowego .


k








      

Podobnie jak w przypadku algorytmu 1.6 (n-ty wyraz cigu Fibonacciego) algorytm ten jest bardzo niewydajny. W wiczeniach Czytelnik zostanie poproszony
o wykazanie, e algorytm oblicza

n
2 1
k
n
wyrazw w celu obliczenia wartoci . Problem polega na tym, e te same rek
alizacje s rozwizywane w kadym wywoaniu rekurencyjnym. Przykadowo
wywoania bin(n1, k1) oraz bin(n1, k) wi si z obliczeniem wartoci
bin(n2, k1) i realizacja ta jest rozwizywana niezalenie w kadym wywoaniu.
Jak wspomniano w podrozdziale 2.8, podejcie typu dziel i zwyciaj nigdy nie
jest wydajne, kiedy realizacja jest dzielona na dwie mniejsze realizacje, ktrych
rozmiar jest zbliony do rozmiaru oryginalnej realizacji.
Poniej opracujemy bardziej wydajny algorytm, wykorzystujcy programowanie
dynamiczne. W rwnaniu 3.1 okrelilimy ju waciwo rekurencyjn. Wykorzystamy j do skonstruowania rozwizania wykorzystujcego tablic B, gdzie
i
B[i][j] bdzie zawiera warto . Poniej opisano kolejne etapy tworzenia algo j
rytmu, opartego na programowaniu dynamicznym.

114

Podstawy algorytmw z przykadami w C++

1. Okrelamy waciwo rekurencyjn. Dokonalimy ju tego w rwnaniu

3.1. Zapisujc je w kontekcie uycia tablicy B otrzymujemy:

B[i 1][ j 1] + B[i 1][ j ]


B[i ][ j ] =
1

0< j<i
j = 0 lub j = i

2. Rozwizujemy realizacj problemu w porzdku wstpujcym, rozpoczynajc

od pierwszego wiersza i wyliczajc po kolei wartoci w wierszach tablicy B.


Na rysunku 3.1 zilustrowano przebieg etapu 2. (Czytelnik powinien rozpozna
w przedstawionej tablicy trjkt Pascala). Kady nastpny wiersz jest wyliczany
na podstawie wartoci wiersza poprzedzajcego przy uyciu waciwoci rekun
rencyjnej, okrelonej w etapie 1. Ostatnia obliczana warto, B[n][k] to .
k
Przykad 3.1 stanowi odzwierciedlenie tych dziaa. Naley zauway, e s
w nim obliczane tylko dwie pierwsze kolumny. Wynika to z faktu, e k = 2 i,
oglnie rzecz biorc, musimy obliczy wartoci w kadym wierszu tylko do k-tej
kolumny. W przykadzie 3.1 obliczono warto B[0][0], poniewa wspczynnik
dwumianowy jest zdefiniowany dla n = k = 0. Std algorytm wykonuje ten etap,
nawet jeli warto nie jest wykorzystywana w dalszych obliczeniach.
Rysunek 3.1.
Tablica B, uywana
do obliczenia
wspczynnika
dwumianowego

Przykad 3.1.

4
Obliczamy warto B[4][2] = .
2
Obliczamy wiersz 0.:

{Etap ten jest wykonywany wycznie w celu


dokadnego odwzorowania algorytmu}.

Rozdzia 3.

Programowanie dynamiczne

115

{Warto B[0][0] nie jest potrzebna w dalszych


obliczeniach}.
B[0][0] = 1
Obliczamy wiersz 1.:

B[1][0] = 1
B[1][1] = 1

Obliczamy wiersz 2.:

B[2][0] = 1
B[2][1] = B[1][0]+B[1][1] = 1+1 = 2
B[2][2] = 1

Obliczamy 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 = 3

Obliczamy 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

W przykadzie 3.1 obliczalimy po kolei coraz wiksze wartoci wspczynnika


dwumianowego. W kadym przebiegu wartoci potrzebne do wykonania biecych
dziaa byy ju obliczone i zachowane. Procedura ta ma fundamentalne znaczenie
dla programowania dynamicznego. Poniszy algorytm stanowi implementacj opisanego podejcia do obliczania wspczynnika dwumianowego.
Algorytm 3.2.

Obliczanie wspczynnika dwumianowego przy uyciu programowania


dynamicznego
Problem: oblicz wspczynnik dwumianowy.
Dane wejciowe: nieujemne liczby cakowite n i k, gdzie k n.

Dane wyjciowe: bin2 warto wspczynnika dwumianowego .


k


  



 

 
 
 

 





    



116

Podstawy algorytmw z przykadami w C++

Parametry n i k nie s rozmiarem danych wejciowych w przypadku tego algorytmu. Stanowi one dane wejciowe, natomiast rozmiarem danych wejciowych
jest liczba symboli uytych do ich zakodowania. Z podobn sytuacj mielimy do
czynienia w podrozdziale 1.3, gdzie omawialimy algorytm obliczajcy n-ty wyraz
cigu Fibonacciego. Jednake wci moemy zyska wgld w wydajno dziaania
algorytmu poprzez okrelenie iloci wykonywanych dziaa w funkcji zmiennych
n i k. Dla danego n i k obliczymy liczb przebiegw ptli HQT-j. W poniszej tabeli
3.1 zawarto liczby przebiegw dla kadej wartoci i.
Tabela 3.1. Liczba przebiegw ptli for-j w zalenoci od wartoci zmiennej i
i

k+1

Liczba przebiegw

k+1

k+1

k+1

Cakowit liczb przebiegw okrela wic zaleno:

1 + 2 + 3 + 4 + K + k + (k + 1) + (k + 1) + K + (k + 1)
14444
4244444
3
n k +1 razy

Wykorzystujc wynik otrzymany w przykadzie A.1 w dodatku A otrzymujemy


nastpujce wyraenie:

k (k + 1)
(2n k + 2)(k + 1)
+ (n k + 1)(k + 1) =
(nk )
2
2
Wykorzystujc programowanie dynamiczne zamiast podejcia typu dziel i zwyciaj udao nam si opracowa znacznie wydajniejszy algorytm. Jak wczeniej
wspomniano, programowanie dynamiczne jest podobne do metody dziel i zwyciaj
o tyle, e szukamy waciwoci rekurencyjnej, ktra dzieli realizacj problemu na
mniejsze realizacje. Rnica polega na tym, e w przypadku programowania dynamicznego wykorzystujemy waciwo rekurencyjn w celu iteracyjnego rozwizania realizacji w kolejnych krokach, rozpoczynajc od najmniejszej realizacji,
zamiast nieuzasadnionego naduywania rekurencji. W ten sposb kad mniejsz
realizacj rozwizujemy tylko raz. Programowanie dynamiczne jest dobrym rozwizaniem do wyprbowania w sytuacji, gdy metoda dziel i zwyciaj daje w wyniku bardzo mao wydajny algorytm.
Najprostszym sposobem przedstawienia algorytmu 3.2 jest utworzenie dwuwymiarowej tablicy B. Jednak kiedy obliczy si wartoci w danym wierszu, nie s
ju potrzebne wartoci obliczone w wierszu poprzednim. Std algorytm mona
zapisa przy uyciu tablicy jednowymiarowej, indeksowanej od 0 do k. Tego rodzaju modyfikacj zawarto w wiczeniach. Kolejnym ulepszeniem algorytmu jest
n n
.
wykorzystanie faktu, e =
k n k

Rozdzia 3.

Programowanie dynamiczne

117

3.2. Algorytm Floyda okrelania


najkrtszej drogi w grafie
Czstym problemem podrnych latajcych samolotami jest znalezienie najkrtszej drogi z jednego miasta do drugiego w przypadku, gdy nie istnieje poczenie
bezporednie. Poniej opracujemy algorytm rozwizywania tego typu problemw.
Najpierw jednak przedstawimy nieformalne wprowadzenie do teorii grafw. Na
rysunku 3.2 przedstawiono waony graf skierowany. W graficznej reprezentacji grafu
kka oznaczaj wierzchoki (ang. vertices), natomiast linie czce jeden wierzchoek z drugim to krawdzie (ang. edges). Jeeli do wszystkich krawdzi zostanie
przypisany odpowiedni kierunek, graf jest okrelany mianem grafu skierowanego
(ang. directed graph) lub digrafu. Rysujc krawd w takim grafie uywamy strzaki
w celu przedstawienia kierunku. W digrafie mog istnie dwie krawdzie midzy
dwoma wierzchokami, kada biegnca w innym kierunku. Na przykad na rysunku 3.2 mamy krawd z wierzchoka v1 do wierzchoka v2 oraz z wierzchoka
v2 do v1. Jeeli krawdzie posiadaj zwizane ze sob wartoci, s one nazywane
wagami (ang. weights), za graf okrela si mianem grafu waonego (ang. weighted
graph). Zakadamy, e wagi s nieujemne. Cho okrela si je zwykle mianem wag,
w wielu zastosowaniach reprezentuj one odlegoci. Std mwimy o drodze z jednego wierzchoka do innego. W grafie skierowanym droga (ang. path) to sekwencja
wierzchokw, taka e istnieje krawd z kadego wierzchoka do jego nastpnika.
Przykadowo, na rysunku 3.2 sekwencja [v1, v4, v3] jest drog, gdy istnieje krawd z wierzchoka v1 do v4 oraz z v4 do v3. Sekwencja [v3, v4, v1] nie jest drog,
gdy nie istnieje krawd z wierzchoka v4 do v1. Droga z wierzchoka do niego
samego nosi nazw cyklu (ang. cycle). Droga [v1, v4, v5, v1] na rysunku 3.2 jest
cyklem. Jeeli graf zawiera cykl, jest grafem cyklicznym (ang. cyclic). W przeciwnym wypadku nosi nazw acyklicznego (ang. acyclic). Droga jest nazywana
drog prost (ang. simple), jeeli nie przechodzi dwa razy przez ten sam wierzchoek. Na rysunku 3.2 droga [v1, v2, v3] jest prosta, ale droga [v1, v4, v5, v1, v2]
nie jest prosta. Naley zauway, e droga prosta nigdy nie zawiera poddrogi,
ktra byaby cykliczna. Dugoci (ang. length) drogi w grafie skierowanym jest
suma wag krawdzi nalecych do drogi. W przypadku grafu niewaonego jest to
po prostu liczba krawdzi nalecych do drogi.
Rysunek 3.2.
Graf skierowany
z podanymi wagami

Czsto spotykanym problemem jest znalezienie najkrtszych drg z kadego wierzchoka do wszystkich innych wierzchokw. Oczywicie, najkrtsza droga musi
by drog prost. Na rysunku 3.2 istniej trzy drogi proste z wierzchoka v1 do v3:
[v1, v2, v3], [v1, v4, v3] oraz [v1, v2, v4, v3]. Z uwagi na fakt, e:

118

Podstawy algorytmw z przykadami w C++

dugo [v1 , v2 , v3 ] = 1 + 3 = 4
dugo [v1 , v4 , v3 ] = 1 + 2 = 3
dugo [v1 , v2 , v4 , v3 ] = 1 + 2 + 2 = 5
najkrtsz drog z v1 do v3 jest [v1, v4, v3]. Jak wczeniej wspomniano, jednym
z czsto wykorzystywanych zastosowa problemu najkrtszej drogi jest okrelanie
najkrtszych tras midzy miastami.
Problem najkrtszej drogi to problem optymalizacji (ang. optimization problem).
Moe istnie wicej ni jedno rozwizanie kandydujce do miana najlepszego dla
problemu optymalizacji. Kade rozwizanie kandydujce posiada zwizan ze sob
warto i rozwizaniem realizacji jest rozwizanie kandydujce o optymalnej wartoci. W zalenoci od problemu wartoci optymaln moe by minimum lub maksimum. W przypadku problemu najkrtszej drogi rozwizaniem kandydujcym (ang.
candidate solution) jest droga z jednego wierzchoka do drugiego, wartoci jest
dugo tej drogi, za wartoci optymaln jest minimum tych dugoci.
Ze wzgldu na fakt, e moe istnie wiele najkrtszych drg z jednego wierzchoka do drugiego, rozwizanie problemu polega na znalezieniu dowolnej z tych
najkrtszych drg. Oczywistym algorytmem rozwizania problemu byoby okrelenie dla kadego wierzchoka dugoci wszystkich drg z niego do innych wierzchokw i znalezienie minimum wrd tych dugoci. Jednak algorytm taki byby
wykonywany w czasie gorszym od wykadniczego. Na przykad zamy, e istnieje krawd z jednego wierzchoka do wszystkich innych wierzchokw. Ponadto podzbir wszystkich drg z tego wierzchoka do innych wierzchokw jest
zbiorem tych wszystkich drg, ktre rozpoczynaj si od pierwszego wierzchoka
i kocz na innych wierzchokach, przechodzc przez wszystkie pozostae. Z uwagi
na fakt, e drugim wierzchokiem w takiej drodze moe by dowolny spord n2
wierzchokw, trzecim wierzchokiem w takiej drodze moe by dowolny spord n3 wierzchokw itd., przedostatnim wierzchokiem w takiej drodze moe
by tylko jeden wierzchoek, cakowita liczba drg z jednego wierzchoka do innych wierzchokw, ktre przechodz przez wszystkie inne wierzchoki, wynosi:

(n 2)(n 3) K1 = (n 2)!
Wynik ten jest gorszy od wykadniczego. Z t sam sytuacj mamy do czynienia
w przypadku wielu problemw optymalizacji, to znaczy oczywisty algorytm rozpatrujcy wszystkie moliwoci jest wykadniczy lub gorszy. Naszym celem jest
znalezienie bardziej wydajnego algorytmu.
Wykorzystujc programowanie dynamiczne opracujemy algorytm wykonywany
w czasie szeciennym, stanowicy rozwizanie problemu najkrtszej drogi. Najpierw opracujemy algorytm okrelajcy tylko dugoci najkrtszych drg. Pniej
zmodyfikujemy go tak, aby dawa na wyjciu rwnie same najkrtsze drogi. Waony graf skierowany, zawierajcy n wierzchokw, reprezentujemy za pomoc
tablicy W, gdzie:

Rozdzia 3.

waga krawdzi,

W [i ][ j ] = ,
0,

Programowanie dynamiczne

119

jeeli istnieje krawd z vi do v j


jeeli nie istnieje krawd z vi do v j
jeeli i = j

O wierzchoku vj mwimy, e jest przylegy (ang. adjacent) do wierzchoka vi wwczas, gdy istnieje krawd z vi do vj. Tak tablic okrela si mianem reprezentacji
grafu w postaci macierzy przylegoci (ang. adjacency matrix). Graf z rysunku 3.2
przedstawiono w tej postaci na rysunku 3.3. Tablica D na rysunku 3.3 zawiera
dugoci najkrtszych drg w grafie. Przykadowo, D[3][5] wynosi 7, poniewa 7 jest
dugoci najkrtszej drogi z v3 do v5. Jeeli bdziemy w stanie znale sposb
obliczania wartoci w D na podstawie wartoci w W, otrzymamy algorytm rozwizujcy problem najkrtszej drogi. Osigniemy to, tworzc sekwencj n+1 tablic
D(k), gdzie 0 k n oraz
D(k)[i][j] = dugo najkrtszej drogi z vi do vj, zawierajcej jako wierzchoki
porednie tylko wierzchoki nalece do zbioru {v1,v2,...vk}.
Rysunek 3.3.
Tablica W
reprezentuje graf
z rysunku 3.2, za
tablica D zawiera
dugoci najkrtszych
drg. Opracowywany
algorytm rozwizania
problemu najkrtszej
drogi oblicza wartoci
w D na podstawie
wartoci w W

Zanim wykaemy, e pozwala nam to na obliczenie wartoci w D na podstawie


wartoci w W, zilustrujemy znaczenie poszczeglnych elementw w tych tablicach.
Przykad 3.2.

Obliczmy pewne przykadowe wartoci w tablicy D(k)[i][j] dla grafu z rysunku 3.2.
D(0)[2][5] = length [v2, v5] =
D(1)[2][5] = minimum (length [v2, v5], length [v2, v1, v5])
minimum (, 14) = 14
D(2)[2][5] = D(1)[2][5] = 14

{Dla dowolnego grafu s one rwne, poniewa}


{najkrtsza droga rozpoczynajca si w v2}
{nie moe przechodzi przez v2.}

D(3)[2][5] = D(2)[2][5] = 14

{Dla tego grafu s one rwne, poniewa}


{uwzgldnienie wierzchoka v3 nie daje adnej}
{nowej drogi z v2 do v5.}

120

Podstawy algorytmw z przykadami w C++

D(4)[2][5] = minimum (length [v2, v1, v5], length [v2, v4, v5])
length [v2, v1, v4, v5], length [v2, v3, v4, v5])
minimum (14, 5, 13, 10)+5
D(5)[2][5] = D(4)[2][5] = 5

{Dla tego grafu s one rwne, poniewa}


{najkrtsza droga koczca si w v5 nie moe}
{przechodzi przez v5.}

Ostatnia obliczona warto, D(5)[2][5], jest dugoci najkrtszej drogi z v2 do v5,


ktra moe przechodzi przez dowolny inny wierzchoek. Oznacza to, e jest ona
dugoci najkrtszej drogi.
D(n)[i][j] jest dugoci najkrtszej drogi z vi do vj, ktra moe przechodzi przez
dowolne wierzchoki, wic jest dugoci najkrtszej drogi z vi do vj. D(0)[i][j]jest
dugoci najkrtszej drogi, ktra nie moe przechodzi przez inne wierzchoki,
wic jest wag przypisan do krawdzi, wiodcej od vi do vj. Okrelilimy, e

D (0) = W

oraz D ( n ) = D

Std w celu okrelenia D na podstawie W musimy jedynie znale sposb otrzymywania wartoci D(n) na podstawie D(0). Poniej opisano poszczeglne etapy zmierzajcych do tego dziaa, w ktrych wykorzystamy programowanie dynamiczne.
1. Okrelamy waciwo rekurencyjn (proces), dziki ktrej moemy obliczy

D(k) na podstawie D(k1).

2. Rozwizujemy realizacj problemu w porzdku wstpujcym poprzez

powtarzanie procesu (okrelonego w etapie 1.) dla k = 1 do n. Daje to


sekwencj:
D 0 , D 1 , D 2 , K , Dn

(3.2)

Etap 1. wykonujemy, rozpatrujc dwa przypadki.


Przypadek 1. Przynajmniej jedna najkrtsza droga z vi do vj, zawierajca jako
wierzchoki porednie tylko wierzchoki ze zbioru {v1, v2, , vk}, nie zawiera
wierzchoka vk. Wwczas

D ( k ) [i ][ j ] = D ( k 1) [i ][ j ]

(3.3)

Przykadem tej sytuacji na rysunku 3.2 jest

D ( 5 ) [1][3] = D ( 4 ) [1][3] = 3
poniewa kiedy uwzgldnimy wierzchoek v5, najkrtsza droga z v1 do v3 to wci
[v1, v4, v3].

Rozdzia 3.

Programowanie dynamiczne

121

Przypadek 2. Wszystkie najkrtsze drogi z vi do vj, zawierajce jako wierzchoki


porednie tylko wierzchoki ze zbioru {v1, v2, , vk}, zawieraj wierzchoek vk.
W takim przypadku dowolna najkrtsza droga ma posta podobn do przedstawionej na rysunku 3.4. vk nie moe by wierzchokiem porednim na poddrodze
z vi do vk, wic taka poddroga zawiera jako wierzchoki porednie jedynie wierzchoki ze zbioru {v1, v2, , vk1}. To implikuje, e dugo poddrogi musi by
rwna D(k1)[i][k] z poniej podanego powodu. Po pierwsze, dugo poddrogi nie
moe by mniejsza, gdy D(k1)[i][k] jest dugoci najkrtszej drogi z vi do vk,
zawierajcej tylko wierzchoki ze zbioru {v1, v2, , vk1}. Po drugie, dugo
poddrogi nie moe by wiksza, poniewa gdyby tak byo, moglibymy j zastpi na rysunku 3.4 najkrtsz drog, co staoby w sprzecznoci z faktem, e caa
droga z rysunku 3.4 jest najkrtsz drog. Podobnie dugo poddrogi z vk do vj
na rysunku 3.4 musi by rwna D(k1)[k][j]. Std w drugim przypadku:

D ( k ) [i ][ j ] = D ( k 1) [i ][k ] + D ( k 1) [k ][ j ]

(3.4)

Rysunek 3.4.
Najkrtsza droga,
zawierajca
wierzchoek vk

Przykadem drugiego przypadku na rysunku 3.2 jest

D ( 2 ) [5][3] = 7 = 4 + 3 = D (1) [5][2] + D (1) [2][3]


Poniewa przypadek 1. lub 2. musi wystpowa, wartoci D(k)[i][j] jest minimum
wartoci, znajdujcych si po prawej stronie rwna 3.3 oraz 3.4. Oznacza to, e
moemy okreli D(k) na podstawie D(k1) w nastpujcy sposb:

D ( k ) [i ][ j ] = minimum( D ( k 1) [i ][ j ], D ( k 1) [i ][k ] + D ( k 1) [k ][ j ])
14243 14444244443
przypadek 1.

przypadek 2.

W ten sposb wykonalimy etap 1. opracowywania algorytmu, wykorzystujcego


programowanie dynamiczne. W celu wykonania etapu 2. wykorzystamy waciwo rekurencyjn z etapu 1. w celu utworzenia sekwencji tablic, przedstawionej
w wyraeniu 3.2. Poniej przyjrzymy si przykadowi sposobu obliczania wartoci kadej z tych tablic na podstawie poprzedniej tablicy.
Przykad 3.3.

Jeli mamy dany graf z rysunku 3.2, reprezentowany przez macierz przylegoci
W na rysunku 3.3, niektre obliczenia wykonuje si w nastpujcy sposb (naley
pamita, e D(0) = W):

122

Podstawy algorytmw z przykadami w C++

D (1) [ 2][4] = minimum( D (0) [2][4], D ( 0) [2][1] + D ( 0) [1][4])


= minimum(2, 9 + 1) = 2
D (1) [5][2] = minimum( D ( 0) [5][2], D (0) [5][1] + D ( 0) [1][2])
= minimum(, 3 + 1) = 4
D (1) [5][4] = minimum( D ( 0) [5][4], D (0) [5][1] + D ( 0) [1][4])
= minimum(, 3 + 1) = 4

Kiedy caa tablica D(1) zostanie wyliczona, obliczamy tablic D(2). Przykadowe
obliczenie ma posta:
D ( 2) [5][4] = minimum( D (1) [5][4], D (1) [5][2] + D (1) [2][4])
= minimum(4, 4 + 2) = 4

Po obliczeniu wszystkich wyrazw w tablicy D(2) kontynuujemy dziaania po kolei


do momentu wyliczenia D(5). Kocow tablic jest D, ktra zawiera dugoci najkrtszych drg. Na rysunku 3.3 umieszczono j po prawej stronie.
Poniej przedstawimy algorytm opracowany przez Floyda (1962), znany jako algorytm Floyda (ang. Floyds algorithm). Pniej wyjanimy, dlaczego korzysta on
tylko z jednej tablicy D oprcz tablicy wejciowej W.
Algorytm 3.3.

Algorytm Floyda okrelania najkrtszej drogi


Problem: oblicz najkrtsze drogi z kadego wierzchoka w grafie waonym do
wszystkich innych wierzchokw. Wagi s liczbami nieujemnymi.
Dane wejciowe: waony graf skierowany oraz n, liczba wierzchokw w grafie.

Graf jest reprezentowany przez tablic dwuwymiarow W, ktrej wiersze i kolumny s indeksowane od 1 do n, gdzie W[i][j] jest wag krawdzi, prowadzcej
od i-tego do j-tego wierzchoka.
Dane wyjciowe: dwuwymiarowa tablica D, ktrej wiersze i kolumny s indeksowane od 1 do n, gdzie D[i][j] jest dugoci najkrtszej drogi, prowadzcej od i-tego
do j-tego wierzchoka.

 




  
 
 
  
   
  
   

 

 

 
     
    



Rozdzia 3.

Programowanie dynamiczne

123

Moemy wykona nasze obliczenia przy uyciu tylko jednej tablicy D, poniewa
wartoci w k-tym wierszu oraz k-tej kolumnie nie s wymieniane w czasie k-tego
przebiegu ptli. Oznacza to, e w k-tym przebiegu algorytm dokonuje przypisania:
D[i ][k ] = minimum( D[i ][k ], D[i ][k ] + D[k ][k ])

co jest oczywicie rwne D[i][k] oraz


D[k ][ j ] = minimum( D[k ][ j ], D[k ][k ] + D[k ][ j ])

co jest oczywicie rwne D[k][j]. W czasie k-tego przebiegu element D[i][j] jest
obliczany tylko na podstawie wasnej wartoci oraz wartoci w k-tym wierszu i k-tej
kolumnie. Wartoci te zostay przypisane w (k1). przebiegu, wic s poszukiwanymi przez nas wartociami. Jak wspomniano wczeniej, czasem po opracowaniu algorytmu programowania dynamicznego istnieje moliwo jego poprawienia, tak aby by bardziej wydajny pod wzgldem zajtoci pamici.
Poniej zostanie przedstawiona analiza algorytmu Floyda.
Analiza
algorytmu
3.3.

Zoono czasowa w kadym przypadku (algorytm Floyda na najkrtsz drog)


Operacja podstawowa: instrukcja w ptli HQT-j.
Rozmiar danych wejciowych: n, liczba wierzchokw w grafie.

Mamy do czynienia z ptl w ptli w ptli, z ktrych kada jest zwizana z wykonaniem n przebiegw, tak wic:

T (n) = n n n = n 3 (n 3 ).
Ponisza modyfikacja algorytmu 3.3 pozwala na obliczenie najkrtszej drogi.
Algorytm 3.4.

Algorytm Floyda okrelania najkrtszej drogi 2


Problem: podobnie jak w przypadku algorytmu 3.3. Oprcz tego tworzone s rwnie

najkrtsze drogi.
Dodatkowe dane wyjciowe: tablica P, ktrej wiersze i kolumny s indeksowane

od 1 do n, gdzie

najwyszy indeks wierzchoka poredniego w najkrtszej drodze

P(i, j ) = od vi do v j , jeeli istnieje co najmniej jeden wierzchoek poredni


0, jeeli nie istnieje aden wierzchoek poredni

 
 



  

124

Podstawy algorytmw z przykadami w C++


  
 
  
   
  
   
  
 
 
  
   
  
   

 

 

    
  

  
  
 
     




Na rysunku 3.5 przedstawiono tablic P, ktra jest tworzona w przypadku zastosowania algorytmu dla grafu z rysunku 3.2.
Rysunek 3.5.
Tablica P, utworzona
w przypadku zastosowania
algorytmu 3.4 dla grafu
z rysunku 3.2

Poniszy algorytm tworzy najkrtsz drog od wierzchoka vq do vr na podstawie


tablicy P.
Algorytm 3.5.

Wywietlenie najkrtszej drogi


Problem: wywietl wierzchoki porednie w najkrtszej drodze od jednego wierzchoka do innego w grafie waonym.
Dane wejciowe: tablica P, utworzona przez algorytm 3.4 oraz dwa indeksy (q i r)

wierzchokw w grafie, ktry stanowi dane wejciowe algorytmu 3.4.

najwyszy indeks wierzchoka poredniego w najkrtszej drodze

P(i, j ) = od vi do v j , jeeli istnieje co najmniej jeden wierzchoek poredni


0, jeeli nie istnieje aden wierzchoek poredni

Dane wyjciowe: wierzchoki porednie w najkrtszej drodze z vq do vr.


 

  
 

   

Rozdzia 3.

Programowanie dynamiczne

125

 



Warto przypomnie konwencj okrelon w rozdziale 2., zgodnie z ktr danymi


wejciowymi podprogramw rekurencyjnych mog by tylko takie zmienne, ktrych warto moe ulega zmianie w wywoaniach rekurencyjnych. Std tablica
P nie stanowi danych wejciowych procedury path. Gdyby algorytm zaimplementowano definiujc P globalnie i chcielibymy okreli najkrtsz drog z wierzchoka
vq do wierzchoka vr, to wywoanie procedury path na najwyszym poziomie
miaoby posta:


Dla danej wartoci P z rysunku 3.5, jeeli wartoci q i r wynosiyby, odpowiednio,


5 i 3, dane wyjciowe miayby posta:
v1 v4
S to wierzchoki porednie w najkrtszej drodze z wierzchoka v5 do wierzchoka v3.
W wiczeniach zostanie okrelone, e W(n) (n) w przypadku algorytmu 3.5.

3.3. Programowanie dynamiczne


a problemy optymalizacyjne
Naley przypomnie, e algorytm 3.4 nie tylko pozwala okreli dugoci najkrtszych drg, ale rwnie konstruuje najkrtsze drogi. Konstrukcja optymalnego
rozwizania jest trzecim etapem w procesie opracowywania algorytmu programowania dynamicznego dla problemu optymalizacji. Oznacza to, e w procesie
opracowywania takiego algorytmu mona wyrni nastpujce etapy:
1. Okrelenie waciwoci rekurencyjnej, ktra daje rozwizanie optymalne

dla realizacji problemu.


2. Obliczenie wartoci rozwizania optymalnego w porzdku wstpujcym.
3. Skonstruowanie rozwizania optymalnego w porzdku wstpujcym.

Etapy 2. oraz 3. s zwykle wykonywane mniej wicej w tym samym miejscu algorytmu. Ze wzgldu na fakt, e algorytm 3.2 nie jest problemem optymalizacji, nie
wystpuje w nim trzeci etap.
Cho moe si wydawa, e problem optymalizacji moe zawsze zosta rozwizany
przy uyciu programowania dynamicznego, nie jest to prawd. Aby tak byo, w przypadku danego problemu musi mie zastosowanie zasada optymalnoci. Zasad t
mona wyrazi nastpujco:

126

Podstawy algorytmw z przykadami w C++

Definicja
O zasadzie optymalnoci (ang. principle of optimality) mwi si, e ma zastosowanie w problemie wwczas, gdy rozwizanie optymalne realizacji problemu
zawsze zawiera rozwizania optymalne dla wszystkich podrealizacji.

Zasada optymalnoci jest trudna do jednoznacznego zdefiniowania i atwiej jest j


zrozumie poprzez analiz konkretnego przykadu. W przypadku problemu najkrtszej drogi pokazalimy, e jeeli vk jest wierzchokiem nalecym do drogi
optymalnej z vi do vj, to poddrogi z vi do vk oraz z vk do vj rwnie musz by
optymalne. Std optymalne rozwizanie realizacji zawiera rozwizania optymalne
wszystkich podrealizacji i ma tu zastosowanie zasada optymalnoci.
Jeeli zasada optymalnoci ma zastosowanie w przypadku danego problemu, moemy okreli waciwo rekurencyjn, ktra bdzie dawa optymalne rozwizanie realizacji w kontekcie optymalnych rozwiza podrealizacji. Istotnym, cho
subtelnym powodem, dla ktrego moemy wwczas wykorzysta programowanie
dynamiczne w celu skonstruowania optymalnego rozwizania realizacji, jest to,
e optymalne rozwizania podrealizacji mog by dowolnymi optymalnymi rozwizaniami. Przykadowo, w przypadku problemu najkrtszej drogi: jeeli poddrogami s dowolne najkrtsze drogi, poczona droga bdzie optymalna. Moemy
wic wykorzysta waciwo rekurencyjn w celu skonstruowania rozwiza optymalnych coraz wikszych realizacji w porzdku wstpujcym. Kade tak otrzymane rozwizanie jest optymalne.
Cho zasada optymalnoci moe wydawa si oczywista, w praktyce konieczne jest
wykazanie, e zasada ma zastosowanie, zanim jeszcze zaoy si, e rozwizanie
optymalne moe zosta otrzymane dziki programowaniu dynamicznemu. Poniszy przykad pokazuje, e nie ma ona zastosowania w przypadku kadego problemu optymalizacji.
Przykad 3.4.

Rozwamy problem najduszej drogi, polegajcy na znalezieniu najduszych prostych drg wiodcych od kadego wierzchoka do wszystkich innych wierzchokw. Problem ograniczamy do prostych drg, poniewa w przypadku cykli zawsze
moemy utworzy dowolnie dug drog poprzez powtarzalne przechodzenie przez
cykl. Na rysunku 3.6 optymalna (najdusza) prosta droga z v1 do v4 to [v1, v3, v2, v4].
Jednak poddroga [v1, v3] nie jest optymaln (najdusz) drog z v1 do v3, poniewa
dugo[v1 , v3 ] = 1 oraz dugo[v1 , v2 , v3 ] = 4

Zatem zasada optymalnoci nie ma zastosowania. Wynika to z faktu, e optymalne


drogi z v1 do v3 oraz z v3 do v4 nie mog zosta razem powizane, aby utworzy
optymaln drog z v1 do v4. Spowodowaoby to utworzenie cyklu, a nie optymalnej drogi.

Rozdzia 3.

Programowanie dynamiczne

127

Rysunek 3.6.
Waony graf
skierowany z cyklem

Pozosta cz rozdziau powicimy problemom optymalizacji. Opracowujc


algorytmy nie bdziemy wymienia wczeniej opisanych etapw dziaania. Powinno
by rzecz oczywist, e postpujemy zgodnie z nimi.

3.4. acuchowe mnoenie macierzy


Zamy, e chcemy pomnoy macierz o wymiarach 23 przez macierz o rozmiarach 34 w sposb nastpujcy:
7 8 9 1
1 2 3
29 35 41 38
4 5 6 2 3 4 5 = 74 89 104 83

6 7 8 9

Macierz wynikowa ma rozmiary 24. Jeeli uyjemy standardowej metody mnoenia macierzy (opartej na definicji mnoenia macierzy), obliczenie kadego
elementu iloczynu wymaga bdzie wykonania trzech podstawowych operacji
mnoenia. Przykadowo, pierwszy element w pierwszej kolumnie to:
11
4
7 +4
22
24+4
34
36
4
3 mnoenia

Ze wzgldu na fakt, e w iloczynie wystpuje 24 = 8 pozycji, cakowita liczba


elementarnych operacji mnoenia wynosi
2 4 3 = 24

Oglnie rzecz biorc, w celu pomnoenia macierzy o wymiarach ij przez macierz o wymiarach jk przy uyciu metody standardowej musimy wykona
i j k elementarnych operacji mnoenia

Rozwamy operacj mnoenia nastpujcych macierzy:

128

Podstawy algorytmw z przykadami w C++

D
20 2
2 30
30 12
12 8

Wymiary kadej macierzy zapisano pod nimi. Mnoenie macierzy jest operacj
czn, co oznacza, e kolejno, w jakiej wykonujemy mnoenie, nie ma znaczenia. Przykadowo, operacje A(B(CD)) oraz (AB)(CD) daj ten sam wynik. Istnieje pi rnych kolejnoci, w jakich moemy pomnoy cztery macierze i ktre
dadz zwykle rn liczb elementarnych operacji mnoenia. W przypadku powyszych macierzy mamy nastpujce liczby elementarnych operacji mnoenia
dla rnych kolejnoci dziaa:
A(B(CD)) 30128+ 230 8+20 28 = 3680
(AB)(CD) 20230+3012 8+20308 = 8880
A((BC)D) 23012+ 212 8+20 28 = 1232
((AB)C)D 20230+203012+20128 = 10320
(A(BC))D 23012+20 2 12+20128 = 3120
Trzecia kolejno jest optymalna w przypadku mnoenia czterech macierzy.
Naszym celem jest opracowanie algorytmu, ktry bdzie okrela optymaln kolejno mnoenia n macierzy. Kolejno ta zaley tylko od rozmiarw macierzy.
Std oprcz n rozmiary te stanowi jedyne dane wejciowe dla algorytmu. Algorytm wykorzystujcy metod siow polegaby na rozwaeniu wszystkich moliwych kolejnoci i wybraniu minimum tak jak postpilimy powyej. Wykaemy, e taki algorytm jest wykonywany co najmniej w czasie wykadniczym.
Niech tn bdzie liczb rnych kolejnoci, w jakich moemy pomnoy n macierzy: A1, A2, , An. Jednym z podzbiorw wszystkich kolejnoci jest zbir kolejnoci, dla ktrych macierz A1 jest ostatni mnoon macierz. Jak pokazano poniej, liczba rnych kolejnoci w tym podzbiorze wynosi tn1, poniewa jest to
liczba kolejnoci, w jakich moemy pomnoy macierze od A2 do An:
A1 ( A2 A3 K An )
14243
t n 1 rnych
kolejnoci

Drugi podzbir wszystkich kolejnoci jest zbiorem kolejnoci, w przypadku ktrych macierz An jest ostatni mnoon macierz. Oczywicie, liczba rnych kolejnoci w tym podzbiorze rwnie wynosi tn1. Std:
t n t n1 + t n1 = 2t n1

Ze wzgldu na fakt, e istnieje tylko jeden sposb pomnoenia dwch macierzy,


t2 = 1. Wykorzystujc techniki opisane w dodatku B moemy rozwiza t rekurencj, wykazujc, e:
t n 2 n2

Rozdzia 3.

Programowanie dynamiczne

129

Nietrudno zauway, e zasada optymalnoci ma zastosowanie w przypadku tego


problemu. Oznacza to, e optymalna kolejno mnoenia n macierzy zawiera optymaln kolejno mnoenia dowolnego podzbioru zbioru n macierzy. Przykadowo, jeeli optymalna kolejno mnoenia szeciu macierzy ma posta
A1 (((( A2 A3 ) A4 ) A5 ) A6 )

to
( A2 A3 ) A4

musi by optymaln kolejnoci mnoenia macierzy od A2 do A4. Oznacza to, e


w celu skonstruowania rozwizania moemy wykorzysta programowanie dynamiczne.
Ze wzgldu na fakt, e mnoymy (k1)-t macierz, Ak1, przez k-t macierz, Ak,
liczba kolumn w Ak1 musi by rwna liczbie wierszy w Ak. Na przykad w przypadku wczeniej omwionego iloczynu pierwsza macierz ma trzy kolumny, za
druga macierz trzy wiersze. Jeeli przyjmiemy, e d0 jest liczb wierszy w A1,
za dk jest liczb kolumn w Ak dla 1 k n, to wymiary Ak bd wynosi dk1dk.
Zilustrowano to na rysunku 3.7.
Rysunek 3.7.
Liczba kolumn
w macierzy Ak1
jest taka sama,
jak liczba wierszy
w macierzy Ak

Tak, jak w poprzednim podrozdziale, w celu skonstruowania rozwizania uyjemy


sekwencji tablic. Dla 1 i j n niech
M[i][j] = minimalna liczba mnoe wymaganych do pomnoenia
macierzy od Ai do Aj, jeeli i < j
M[i][i] = 0
Zanim omwimy sposb uycia tych tablic, poniej zilustrujemy znaczenie zawartych w nich elementw.
Przykad 3.5.

Zamy, e posiadamy nastpujce sze macierzy:

A1
A2
5 2
23
d 0 d1
d1 d 2

A3
3 4
d2 d3

A4
46
d3 d4

A5
67
d4 d5

A6
78
d5 d6

W celu pomnoenia macierzy A4, A5 i A6 moemy okreli ponisze dwie kolejnoci oraz liczby elementarnych operacji mnoenia:

130

Podstawy algorytmw z przykadami w C++

( A4 A5 ) A6

Liczba operacji mnoenia = d 3 d 4 d 5 + d 3 d 5 d 6


= 4 6 7 + 4 7 8 = 392

A4 ( A5 A6 ) Liczba operacji mnoenia = d 4 d 5 d 6 + d 3 d 4 d 6


= 6 7 8 + 4 6 8 = 528

Std
M [4][6] = minimum(392, 528) = 392

Optymalna kolejno mnoenia szeciu macierzy musi mie jeden z nastpujcych rozkadw:
1. A1(A2A3A4A5A6)
2. (A1A2)(A3A4A5A6)
3. (A1A2A3)(A4A5A6)
4. (A1A2A3A4)(A5A6)
5. (A1A2A3A4A5)A6

gdzie w kadym nawiasie iloczyn jest otrzymywany zgodnie z optymaln kolejnoci dla liczby macierzy, znajdujcych si w tym nawiasie. Spord rozkadw
ten, ktry daje minimaln liczb operacji mnoenia, musi by optymalny. Liczba
operacji mnoenia dla k-tego rozkadu jest minimaln liczb potrzebn do otrzymania kadego czynnika, powikszon o liczb potrzebn do pomnoenia dwch
czynnikw. Oznacza to, e wynosi ona
M [1][k ] + M [k + 1][6] + d 0 d k d 6

Okrelilimy, e
M [1][6] = minimum
1424
3 ( M [1][k ] + M [k + 1][6] + d 0 d k d 6 )
1 k 5

W powyszym rozumowaniu nie ma nic, co wymuszaoby, aby pierwsz macierz


bya A1 lub aby ostatni macierz bya A6. Przykadowo, moglibymy otrzyma podobny rezultat mnoc macierz A2 przez A6. Zatem moemy uoglni ten rezultat
w celu otrzymania nastpujcej waciwoci rekurencyjnej, zwizanej z mnoeniem n macierzy. Dla 1 i j n

M [i][ j ] = minimum
1424
3 ( M [i ][k ] + M [k + 1][ j ] + d i 1d k d j ), jeeli i < j
i k j 1

(3.5)

M [i ][i ] = 0
Algorytm typu dziel i zwyciaj oparty na tej waciwoci jest wykonywany w czasie
wykadniczym. Poniej opracujemy wydajniejszy algorytm, wykorzystujc programowanie dynamiczne w celu obliczenia wartoci M[i][j] w kolejnych etapach.

Rozdzia 3.

Programowanie dynamiczne

131

Uywana jest siatka podobna do trjkta Pascala (patrz podrozdzia 3.1). Obliczenia, ktre s nieco bardziej skomplikowane, ni miao to miejsce w podrozdziale 3.1, s oparte na poniej opisanej waciwoci z rwnania 3.5. Element
M[i][j] jest obliczany na podstawie wszystkich wpisw ze swojego wiersza, znajdujcych si po jego lewej stronie, oraz wpisw ze swojej kolumny, znajdujcych
si poniej niego. Wykorzystujc t waciwo mona obliczy wartoci elementw w M w poniej opisany sposb. Najpierw ustawiamy warto tych elementw
na gwnej przektnej na 0. Nastpnie obliczamy wszystkie elementy na przektnej powyej (nazywamy j przektn 1). Nastpnie obliczamy wszystkie wartoci
na przektnej 2 itd. Kontynuujemy te dziaania do momentu, a obliczymy jedyn warto na przektnej 5, ktra jest nasz odpowiedzi kocow, M[1][6]. Procedur t zilustrowano na rysunku 3.8 dla macierzy z przykadu 3.5. Poniszy
przykad zawiera odpowiednie obliczenia.
Rysunek 3.8.
Tablica M opracowana
w przykadzie 3.5.
Element M[1][4],
ktry oznaczono kkiem,
jest obliczany
na podstawie par
wskazanych wartoci

Przykad 3.6.

Zamy, e mamy sze macierzy okrelonych w przykadzie 3.5. Poniej opiszemy


kolejne dziaania wykonywane przez algorytm programowania dynamicznego. Odpowiednie wyniki przedstawiono na rysunku 3.8.
Obliczamy przektn 0:
M [i ][i ] = 0 dla 1 i 6

Obliczamy przektn 1:
M [1][2] = minimum
1424
3 ( M [1][k ] + M [k + 1][2] + d 0 d k d 2 )
1 k 1

= M [1][1] + M [2][2] + d 0 d 1 d 2
= 0 + 0 + 5 2 3 = 30

132

Podstawy algorytmw z przykadami w C++

Wartoci M[2][3], M[3][4], M[4][5] oraz M[5][6] s obliczane w ten sam


sposb. Przedstawiono je na rysunku 3.8.
Obliczamy przektn 2:
M [1][3] = minimum
1424
3 ( M [1][k ] + M [k + 1][3] + d 0 d k d 3 )
1 k 2

= minimum( M [1][1] + M [2][3] + d 0 d 1 d 3 ,


M [1][2] + M [3][3] + d 0 d 2 d 3
= minimum(0 + 24 + 5 2 4, 30 + 0 + 5 3 4) = 64

Wartoci M[2][4], M[3][5] oraz M[4][6] s obliczane w ten sam sposb.


Przedstawiono je na rysunku 3.8.
Obliczamy przektn 3:
M [1][4] = minimum
1424
3 ( M [1][k ] + M [k + 1][4] + d 0 d k d 4 )
1 k 3

= minimum( M [1][1] + M [2][4] + d 0 d 1 d 4 ,


M [1][2] + M [3][4] + d 0 d 2 d 4 ,
M [1][3] + M [4][4] + d 0 d 3 d 4 )
= minimum(0 + 72 + 5 2 6, 30 + 72 + 5 3 6, 64 + 0 + 5 4 6) = 132

Wartoci M[2][5] oraz M[3][6] s obliczane w ten sam sposb.


Przedstawiono je na rysunku 3.8.
Obliczamy przektn 4:
Wartoci na przektnej 4 s obliczane w ten sam sposb i przedstawiono
je na rysunku 3.8.
Obliczamy przektn 5:
Warto na przektnej 5 jest obliczana w ten sam sposb. Jest ona
rozwizaniem realizacji problemu: jest to minimalna liczba elementarnych
operacji mnoenia i wynosi ona:
M [1][6] = 348

Przedstawiony poniej algorytm stanowi implementacj tej metody. Wymiary n macierzy, a konkretnie wartoci od d0 do dn, s jedynymi danymi wejciowymi algorytmu. Same macierze nie stanowi danych wejciowych, poniewa wartoci w macierzach nie maj znaczenia dla istoty problemu. Tablica P utworzona przez algorytm
moe zosta wykorzystana do wydrukowania optymalnej kolejnoci. Zostanie to
omwione po przeanalizowaniu algorytmu 3.6.

Rozdzia 3.

Algorytm 3.6.

Programowanie dynamiczne

133

Minimalna liczba operacji mnoenia


Problem: okrel minimaln liczb elementarnych operacji mnoenia, wymaganych
w celu pomnoenia n macierzy oraz kolejno wykonywanych mnoe, ktra zapewnia minimaln liczb operacji.
Dane wejciowe: liczba macierzy n oraz tablica liczb cakowitych d, indeksowana

od 0 do n, gdzie d[i1]d[i] jest rozmiarem i-tej macierzy.


Dane wyjciowe: minmult, minimalna liczba elementarnych operacji mnoenia,

wymaganych w celu pomnoenia n macierzy; dwuwymiarowa tablica P, na podstawie ktrej mona okreli optymaln kolejno. P posiada wiersze indeksowane od
1 do n1 oraz kolumny indeksowane od 1 do n. P[i][j] jest punktem, w ktrym macierze od i do j zostaj rozdzielone w kolejnoci optymalnej dla mnoenia macierzy.






 
  





      
 
              
         !"#$
     %
  
 
   &
& 

  $'()
#"***
+
   
+

Poniej dokonamy analizy algorytmu 3.6.


Analiza
algorytmu
3.6.

Zoono czasowa w kadym przypadku (minimalna liczba operacji mnoenia)


Operacja podstawowa: jako operacj podstawow moemy traktowa instrukcje,
wykonywane dla kadej wartoci k. Uwzgldniono rwnie porwnanie sprawdzajce, czy warto jest minimalna.
Rozmiar danych wejciowych: n, liczba macierzy, ktre maj zosta pomnoone.

Mamy do czynienia z ptl w ptli w ptli. Ze wzgldu na fakt, e j = i+diagonal,


dla danych wartoci diagonal oraz i liczba przebiegw ptli k wynosi

j 1 i + 1 = i + diagonal 1 i + 1 = diagonal
Dla danej wartoci diagonal liczba przebiegw ptli HQT-i wynosi ndiagonal. Ze
wzgldu na fakt, e diagonal moe przyjmowa wartoci od 1 do n1, cakowita
liczba powtrze operacji podstawowej wynosi

134

Podstawy algorytmw z przykadami w C++

n 1

[(n diagonal ) diagonal ]

diagonal =1

W wiczeniach zostanie wykazane, e wyraenie to jest rwne:

n(n 1)(n + 1)
( n 3 )
6
Poniej pokaemy, w jaki sposb mona otrzyma optymaln kolejno na podstawie tablicy P. Wartoci zawarte w tej tablicy w momencie, gdy algorytm jest
stosowany dla wymiarw z przykadu 3.5, przedstawiono na rysunku 3.9. Fakt,
e na przykad P[2][5] = 4 oznacza, e optymalna kolejno dla mnoenia macierzy
od A2 do A5 posiada rozkad:

( A2 A3 A4 ) A5
gdzie macierze znajdujce si w nawiasie s mnoone w kolejnoci optymalnej.
Oznacza to, e P[2][5], czyli 4, jest punktem, w ktrym macierze powinny zosta
rozdzielone w celu otrzymania czynnikw. Moemy okreli optymaln kolejno,
odwiedzajc najpierw element P[1][n] w celu okrelenia rozkadu na najwyszym
poziomie. Ze wzgldu na fakt, e n = 6 oraz P[1][6] = 1, rozkad na najwyszym
poziomie dla kolejnoci optymalnej ma posta:

A1 ( A2 A3 A4 A5 A6 )
Rysunek 3.9.
Tablica P, utworzona
w momencie,
gdy algorytm 3.6
zosta zastosowany
wzgldem wymiarw
z przykadu 3.5

Nastpnie okrelamy rozkad w kolejnoci optymalnej dla mnoenia macierzy od A2


do A6, odwiedzajc element P[2][6]. Jego wartoci jest 5, wic rozkad ma posta:

( A2 A3 A4 A5 ) A6
Wiemy, e rozkad w kolejnoci optymalnej ma posta:

A1 (( A2 A3 A4 A5 ) A6 )
gdzie rozkad dla mnoenia macierzy od A2 do A5 wci naley okreli. Sprawdzamy warto elementu P[2][5] i kontynuujemy w ten sposb dalsze dziaania do
momentu, w ktrym zostan okrelone wszystkie rozkady. Odpowied ma posta:

A1 ((( A2 A3 ) A4 ) A5 ) A6 )

Rozdzia 3.

Programowanie dynamiczne

135

Poniszy algorytm stanowi implementacj opisanej metody.


Algorytm 3.7.

Wywietlanie optymalnej kolejnoci


Problem: wywietl optymaln kolejno dla mnoenia n macierzy.
Dane wejciowe: dodatnia liczba cakowita n oraz tablica P, utworzona przez algo-

rytm 3.6. P[i][j] jest punktem, w ktrym macierze od i do j s rozdzielane w kolejnoci optymalnej dla mnoenia tych macierzy.
Dane wyjciowe: optymalna kolejno mnoenia macierzy.
 

 

   
  ,, 
  

 
  ,,
 

 
 
  ,,
+
+

Zgodnie z konwencjami przyjtymi odnonie pisania funkcji rekurencyjnych, P i n nie


s danymi wejciowymi dla procedury order, ale s danymi wejciowymi algorytmu. Gdyby algorytm zaimplementowano poprzez zdefiniowanie P i n globalnie,
wywoanie procedury order na najwyszym poziomie miaoby posta:
 

Kiedy wymiary s takie same, jak w przykadzie 3.5, algorytm wywietla nastpujce informacje:
(A1((((A2A3)A4)A5)A6))
Cae wyraenie ujto w nawiasy, poniewa algorytm wstawia je wok kadego
skadnika zoonego. W wiczeniach zostanie wykazane, e dla algorytmu 3.7

T ( n ) ( n )
Opracowany algorytm (n3) dla acuchowego mnoenia macierzy pochodzi z pracy
Godbolea (1973). Yao (1982) opracowa metody przyspieszajce pewne rozwizania programowania dynamicznego. Wykorzystujc te metody mona utworzy
algorytm (n2) dla acuchowego mnoenia macierzy. Hu oraz Shing (1982, 1984)
opisuj algorytm (n lg n) dla acuchowego mnoenia macierzy.

136

Podstawy algorytmw z przykadami w C++

3.5. Optymalne drzewa


wyszukiwania binarnego
Kolejny algorytm, jaki opracujemy, suy do okrelania optymalnego sposobu zorganizowania zbioru elementw w postaci drzewa wyszukiwania binarnego. Zanim
omwimy, jaka forma organizacji jest uwaana za optymaln, przedstawimy pewne
oglne informacje na temat takich drzew. Dla kadego wierzchoka w drzewie binarnym poddrzewo, ktrego korzeniem jest lewy potomek tego wierzchoka, nosi
nazw lewego poddrzewa (ang. left subtree) wierzchoka. Lewe poddrzewo korzenia drzewa nosi nazw lewego poddrzewa drzewa. Analogicznie definiuje si
prawe poddrzewo (ang. right subtree).
Definicja
Drzewo wyszukiwania binarnego (ang. binary search tree) jest binarnym drzewem elementw (zwykle nazywanych kluczami) pochodzcych ze zbioru uporzdkowanego. Musi spenia nastpujce warunki:
1. Kady wierzchoek zawiera jeden klucz.
2. Kady klucz w lewym poddrzewie danego wierzchoka jest mniejszy lub
rwny kluczowi tego wierzchoka.
3. Klucze znajdujce si w prawym poddrzewie danego wierzchoka s wiksze
lub rwne kluczowi tego wierzchoka.

Na rysunku 3.10 przedstawiono dwa drzewa wyszukiwania binarnego, kade z tymi


samymi kluczami. W drzewie po lewej stronie przyjrzyjmy si prawemu poddrzewu
wierzchoka, zawierajcego klucz Rudolf. Poddrzewo to zawiera klucze Tomasz,
Urszula oraz Waldemar i wszystkie te imiona s wiksze od Rudolfa, zgodnie
z porzdkiem alfabetycznym. Cho oglnie klucz moe wystpowa w drzewie
binarnym wicej ni raz, dla uproszczenia zakadamy, e klucze s unikatowe.
Rysunek 3.10.
Dwa drzewa
przeszukiwania
binarnego

Rozdzia 3.

Programowanie dynamiczne

137

Gboko (ang. depth) wierzchoka w drzewie jest liczb krawdzi w unikatowej


drodze, wiodcej od korzenia do tego wierzchoka. Jest ona rwnie zwana poziomem (ang. level) wierzchoka w drzewie. Zwykle mwimy, e wierzchoek posiada gboko oraz e znajduje si na poziomie. Przykadowo, w drzewie znajdujcym si po lewej stronie na rysunku 3.10 wierzchoek zawierajcy klucz Urszula
posiada gboko 2. Moemy rwnie stwierdzi, e znajduje si on na poziomie 2. Korze posiada gboko 0 i znajduje si na poziomie 0. Gboko drzewa
to maksymalna gboko wszystkich wierzchokw. Drzewo znajdujce si po
lewej stronie na rysunku 3.10 posiada gboko 3, natomiast drzewo znajdujce
si po prawej stronie posiada gboko 2. Drzewo binarne jest nazywane zrwnowaonym (ang. balanced), jeeli gboko dwch poddrzew kadego wierzchoka nigdy nie rni si o wicej ni 1. Drzewo znajdujce si po lewej stronie
na rysunku 3.10 nie jest zrwnowaone, poniewa lewe poddrzewo korzenia posiada gboko 0, natomiast prawe poddrzewo posiada gboko 2. Drzewo znajdujce si po prawej stronie jest zrwnowaone.
Zazwyczaj drzewo wyszukiwania binarnego zawiera pozycje, ktre s pobierane
zgodnie z wartociami kluczy. Naszym celem jest takie zorganizowanie kluczy
w drzewie wyszukiwania binarnego, aby redni czas zlokalizowania klucza by
minimalny (patrz podrozdzia A.8.2, gdzie omwiono problem wartoci redniej).
Drzewo, ktre jest zorganizowane w ten sposb, jest zwane optymalnym. Nietrudno
zauway, e jeeli wszystkie klucze charakteryzuje to samo prawdopodobiestwo zostania kluczem wyszukiwania (ang. search key), to drzewo znajdujce si po
prawej stronie rysunku 3.10 jest optymalne. Jestemy zainteresowani przypadkiem,
gdy klucze nie charakteryzuj si tym samym prawdopodobiestwem. Przykadem
takiego przypadku byoby przeszukanie jednego z drzew z rysunku 3.10 w poszukiwaniu losowo wybranego imienia mieszkacw Polski. Imi Tomasz wystpuje
czciej ni Urszula, wic naleaoby przypisa mu wiksze prawdopodobiestwo
(w podrozdziale A.8.1 w dodatku A zawarto omwienie problematyki losowoci).
Omwimy przypadek, w ktrym wiadomo, e klucz wyszukiwania wystpuje
w drzewie. Uoglnienie, w przypadku ktrego klucz moe nie wystpowa w drzewie, zostanie bliej zbadane w wiczeniach. W celu zminimalizowania redniego
czasu wyszukiwania musimy zna zoono czasow operacji lokalizowania
klucza. Dlatego, zanim przejdziemy dalej, zapiszemy i przeanalizujemy algorytm,
ktry suy do wyszukiwania klucza w drzewie wyszukiwania binarnego. Algorytm wykorzystuje nastpujc struktur danych:
 '-%

  

  &
  & 
+
    &'.%'

Deklaracja ta oznacza, e zmienna typu  jest wskanikiem do rekordu


typu 
. Oznacza to, e jej wartoci jest adres pamici takiego rekordu.

138

Podstawy algorytmw z przykadami w C++

Algorytm 3.8.

Przeszukiwanie drzewa binarnego


Problem: okrel wierzchoek zawierajcy klucz w drzewie wyszukiwania binarnego.

Zakada si, e klucz wystpuje w drzewie.


Dane wejciowe: wskanik tree do drzewa wyszukiwania binarnego oraz klucz

keyin.
Dane wyjciowe: wskanik p do wierzchoka zawierajcego klucz.
   
  

  /

 
 
  01
 2 
 34
 

  
   
 34

 34(5'1$!'%''*

 
 34 (5'%$!'%''*

Liczba porwna wykonywanych przez procedur search w celu zlokalizowania


klucza nosi nazw czasu wyszukiwania (ang. search time). Naszym celem jest okrelenie drzewa, dla ktrego redni czas wyszukiwania jest najmniejszy. Zgodnie
z dyskusj zawart w podrozdziale 1.2 zakadamy, e porwnania s implementowane w sposb wydajny. Przy tym zaoeniu w kadym przebiegu ptli  
wykonywane jest tylko jedno porwnanie. Std czas wyszukiwania danego klucza
wynosi

depth(key) + 1
gdzie depth(key) jest gbokoci wierzchoka zawierajcego klucz. Przykadowo,
ze wzgldu na fakt, e gboko klucza zawierajcego warto Urszula wynosi
2 w lewym poddrzewie na rysunku 3.10, czas wyszukiwania klucza Urszula wynosi

depth(Urszula) + 1 = 2 + 1 = 3
Niech Key1, Key2, , Keyn bd n uporzdkowanymi kluczami oraz niech pi bdzie
prawdopodobiestwem tego, e Keyi jest kluczem wyszukiwania. Jeeli ci oznacza liczb porwna koniecznych do znalezienia klucza Keyi w danym drzewie,
to redni czas wyszukiwania dla tego drzewa wynosi
n

c p
i =1

Jest to warto, ktr chcemy zminimalizowa.

Rozdzia 3.

Przykad 3.7.

Programowanie dynamiczne

139

Rysunek 3.11 przedstawia pi rnych drzew dla n = 3. Faktyczne wartoci kluczy


nie s istotne. Jedynym wymaganiem jest to, aby byy one uporzdkowane. Jeeli
p1 = 0,7

p 2 = 0,2 oraz

p 3 = 0,1

to rednie czasy wyszukiwania dla drzew z rysunku 3.11 wynosz:


1. 3(0,7)+2(0,2)+1(0,1) = 2,6
2. 2(0,7)+3(0,2)+1(0,1) = 2,1
3. 2(0,7)+1(0,2)+2(0,1) = 1,8
4. 1(0,7)+3(0,2)+2(0,1) = 1,5
5. 1(0,7)+2(0,2)+3(0,1) = 1,4
Rysunek 3.11.
Moliwe drzewa
wyszukiwania
binarnego
dla przypadku
trzech kluczy

Pite drzewo jest optymalne.


Oglnie rzecz biorc, nie moemy znale optymalnego drzewa wyszukiwania
binarnego poprzez rozpatrzenie wszystkich drzew wyszukiwania binarnego, gdy
liczba takich drzew jest co najmniej wykadnicza w stosunku do n. Udowodnimy
to wykazujc, e jeeli tylko rozwaymy wszystkie drzewa wyszukiwania binarnego o gbokoci n1, otrzymamy wykadnicz liczb drzew. W drzewie wyszukiwania binarnego o gbokoci n1 pojedynczy wierzchoek na kadym z n1
poziomw (oprcz korzenia) moe znajdowa albo na lewo, albo na prawo od

140

Podstawy algorytmw z przykadami w C++

swojego rodzica, co oznacza, e istniej dwie moliwoci na kadym z tych poziomw. To oznacza, e liczba rnych drzew wyszukiwania binarnego o gbokoci n1 wynosi 2n1.
Wykorzystamy programowanie dynamiczne do opracowania bardziej wydajnego
algorytmu. Zamy, e klucze od Keyi do Keyj s uoone w drzewie, ktre minimalizuje
j

c
m =i

pm

gdzie cm jest liczb porwna, wymaganych do zlokalizowania klucza Keym


w drzewie. Takie drzewo bdziemy nazywa optymalnym dla tych kluczy. Warto optymaln bdziemy oznacza przez A[i][j]. Ze wzgldu na fakt, e zlokalizowanie klucza w drzewie zawierajcym jeden klucz wymaga jednego porwnania, A[i][i] = pi.
Przykad 3.8.

Zamy, e mamy trzy klucze oraz prawdopodobiestwa okrelone w przykadzie 3.7, to znaczy:
p1 = 0,7

p 2 = 0,2 oraz

p 3 = 0,1

W celu okrelenia A[2][3] musimy rozway dwa drzewa z rysunku 3.12. Dla
tych dwch drzew otrzymujemy:
1. 1(p2)+2(p3) = 1(0,2)+2(0,1) = 0,4
2. 2(p2)+1(p3) = 2(0,2)+1(0,1) = 0,5
Rysunek 3.12.
Drzewa wyszukiwania
binarnego utworzone
z kluczy Key2 oraz Key3

Pierwsze drzewo jest optymalne i


A[ 2][3] = 0,4

Naley zauway, e drzewo optymalne otrzymane w przykadzie 3.8 stanowi prawe


poddrzewo korzenia drzewa optymalnego, otrzymanego w przykadzie 3.7. Nawet
gdyby nie byo ono dokadnie takie samo, jak prawe poddrzewo, zwizany z nim
redni czas wyszukiwania musiaby by taki sam. W przeciwnym wypadku moglibymy zastpi drzewem optymalnym z przykadu 3.8 wspomniane prawe poddrzewo z przykadu 3.7, otrzymujc w wyniku drzewo o krtszym rednim czasie
wyszukiwania. Oglnie rzecz biorc, dowolne poddrzewo drzewa optymalnego

Rozdzia 3.

Programowanie dynamiczne

141

musi by optymalne dla kluczy w tym poddrzewie. Zatem zachowana zostaje zasada optymalnoci.
Niech drzewo 1 bdzie drzewem optymalnym przy uwzgldnieniu ograniczenia
mwicego o tym, e klucz Key1 znajduje si w korzeniu, drzewo 2 niech bdzie
drzewem optymalnym przy uwzgldnieniu ograniczenia mwicego o tym, e klucz
Key2 znajduje si w korzeniu, , niech drzewo n bdzie drzewem optymalnym
przy uwzgldnieniu ograniczenia mwicego o tym, e klucz Keyn znajduje si
w korzeniu. Dla 1 k n poddrzewa drzewa k musz by optymalne, a wic
rednie czasy wyszukiwania w tych poddrzewach s zgodne z tym, co przedstawiono na rysunku 3.13. Rysunek ten pokazuje rwnie, e dla kadego m k
wymagana jest dokadnie o jeden wiksza liczba porwna w celu zlokalizowania
klucza Keym w drzewie k ni w celu zlokalizowania tego klucza w poddrzewie,
w ktrym si on znajduje (dodatkowe porwnanie jest zwizane z korzeniem). Owo
dodatkowe porwnanie dodaje 1pm do redniego czasu wyszukiwania klucza Keym
w drzewie k. Okrelilimy, e redni czas wyszukiwania dla drzewa k wynosi:
A[1][k 1] +
14243
redni czas
w lewym poddrzewie

p + K pk 1
11 4243

Dodatkowy czas zwizany


z porwnaniem w korzeniu

p
{k
redni czas
wyszukania korzenia

A[k + 1][n] +
14243
redni czas
w prawym poddrzewie

p +1 + K pn
1k 42
43

Dodatkowy czas zwizany


z porwnaniem w korzeniu

co jest rwnowane:
n

A[1][k 1] + A[k + 1][n] + p m


m =1

Rysunek 3.13.
Optymalne drzewo
wyszukiwania
binarnego przy
zaoeniu, e klucz
Keyk jest korzeniem

Ze wzgldu na fakt, e jedno z k drzew musi by optymalne, redni czas wyszukiwania optymalnego drzewa okrela zaleno:
n

A[1][n] = minimum
1424
3 ( A[1][k 1] + A[k + 1][n]) + p m
1 k n

m =1

gdzie A[1][0] i A[n+1][n] s z definicji rwne 0. Chocia suma prawdopodobiestw w ostatnim wyraeniu wynosi bez wtpienia 1, zapisalimy j jako sum,
poniewa teraz chcemy uoglni rezultat. W powyszej dyskusji nie ma nic, co
wymagaoby, aby klucze miay wartoci od Key1 do Keyn. Oznacza to, e dyskusja
odnosi si oglnie do kluczy od Keyi do Keyj, gdzie i < j. W ten sposb otrzymujemy:

142

Podstawy algorytmw z przykadami w C++

j
A[1][ j ] = minimum
1424
3 ( A[i ][k 1] + A[k + 1][ j ]) + m=i pm i < j
i k j

A[i ][i ] = pi

(3.6)

A[i ][i 1] oraz A[ j + 1][ j ] s z definicji rwne 0

Wykorzystujc zalenoci 3.6 moemy zapisa algorytm sucy do okrelania


optymalnego drzewa wyszukiwania binarnego. Ze wzgldu na fakt, e warto
A[i][j] jest obliczana na podstawie elementw z i-tego wiersza, ale na lewo od
elementu A[i][j] oraz na podstawie elementw z j-tej kolumny, ale poniej elementu A[i][j], kontynuujemy dziaania, obliczajc po kolei wartoci na kadej
przektnej (podobnie, jak miao to miejsce w przypadku algorytmu 3.6). Poszczeglne etapy algorytmu s tak podobne do zawartych w algorytmie 3.6, e
nie zamiecimy przykadu ilustrujcego wykonywane operacje. Zamiast tego podamy po prostu algorytm, po ktrym zostanie przedstawiony obszerny przykad,
prezentujcy rezultaty jego zastosowania. Tablica R utworzona przez algorytm
zawiera indeksy kluczy wybranych dla korzenia w kadym etapie. Przykadowo,
R[1][2] jest indeksem klucza znajdujcego si w korzeniu drzewa optymalnego,
zawierajcego pierwsze dwa klucze, natomiast R[2][4] jest indeksem klucza znajdujcego si w korzeniu drzewa optymalnego zawierajcego drugi, trzeci i czwarty
klucz. Po przeanalizowaniu algorytmu omwimy sposb budowania drzewa optymalnego na podstawie tablicy R.
Algorytm 3.9.

Optymalne drzewo przeszukiwania binarnego


Problem: okrel optymalne drzewo wyszukiwania binarnego dla zbioru kluczy,

z ktrych kady posiada przypisane odpowiednie prawdopodobiestwo zostania


kluczem wyszukiwania.
Dane wejciowe: n, liczba kluczy, oraz tablica liczb rzeczywistych p indeksowana

od 1 do n, gdzie p[i] jest prawdopodobiestwem wyszukiwania i-tego klucza.


Dane wyjciowe: zmienna minavg, ktrej wartoci jest redni czas wyszukiwania

optymalnego drzewa wyszukiwania binarnego oraz dwuwymiarowa tablica R, na


podstawie ktrej mona skonstruowa drzewo optymalne. R posiada wiersze indeksowane od 1 do n+1 oraz kolumny indeksowane od 0 do n. R[i][j] jest indeksem
klucza znajdujcego si w korzeniu drzewa optymalnego, zawierajcego klucze
od i-tego do j-tego.
 

 
 

 
 
 

   


 





 
 
 
  

   
  

Rozdzia 3.

Programowanie dynamiczne

143

  











 

 


 
 

 
 
 
  
 !"
#$%&'(
! !$)*
j

 
 


 

p
m =i

 ')+,&%-!!--


 




Analiza
algorytmu
3.9.

Zoono czasowa w kadym przypadku


(optymalne drzewo wyszukiwania binarnego)
Operacja podstawowa: instrukcje wykonywane dla kadej wartoci k. W ich skad
wchodzi porwnanie sprawdzajce wystpowanie wartoci minimalnej. Warto sumy
j

p
m =i

nie musi by obliczana za kadym razem od pocztku. W wiczeniach Czy-

telnik zostanie poproszony o znalezienie wydajnego sposobu obliczania tych sum.


Rozmiar danych wejciowych: n, liczba kluczy.

Sterowanie tym algorytmem jest niemal identyczne, jak w przypadku algorytmu 3.6.
Jedyna rnica polega na tym, e dla danych wartoci diagonal oraz i operacja
podstawowa jest wykonywana diagonal+1 razy. Analiza, podobna jak w przypadku algorytmu 3.6, pozwala okreli, e:

T ( n) =

n(n 1)(n + 4)
( n 3 )
6

Poniszy algorytm konstruuje drzewo binarne na podstawie tablicy R. Naley


przypomnie, e tablica R zawiera indeksy kluczy wybranych w kadym etapie
jako korzenie.
Algorytm 3.10.

Budowa optymalnego drzewa przeszukiwania binarnego


Problem: zbuduj optymalne drzewo wyszukiwania binarnego.
Dane wejciowe: n, liczba kluczy, tablica Key, zawierajca n uporzdkowanych

kluczy oraz tablica R, utworzona przez algorytm 3.9. R[i][j] jest indeksem klucza
w korzeniu drzewa optymalnego, zawierajcego klucze od i-tego do j-tego.
Dane wyjciowe: wskanik tree do optymalnego drzewa wyszukiwania binarnego,
zawierajcego n kluczy.

144

Podstawy algorytmw z przykadami w C++

    



 
  
 

 
 ./00

  
1
1 

1  

 



Instrukcja RPGYPQFGV[RG tworzy nowy wierzchoek i umieszcza jego adres w p.


Zgodnie z przyjtymi konwencjami zapisu algorytmw rekurencyjnych parametry n, Key oraz R nie s danymi wejciowymi funkcji tree. Gdyby algorytm zaimplementowano definiujc n, Key oraz R globalnie, wskanik root do korzenia
optymalnego drzewa wyszukiwania binarnego byby otrzymywany poprzez wywoanie funkcji tree w nastpujcy sposb:




Nie zilustrowalimy dziaa wykonywanych przez algorytm 3.9, poniewa s one


podobne, jak w przypadku algorytmu 3.6 (minimalna liczba operacji mnoenia).
Podobnie i tym razem nie bdziemy ilustrowa dziaa wykonywanych przez algorytm 3.10, poniewa jest on podobny do algorytmu 3.7 (wywietlenie kolejnoci
optymalnej). Zamiast tego przedstawimy obszerny przykad, obrazujcy wyniki
zastosowania algorytmw 3.9 oraz 3.10.
Przykad 3.9.

Zamy, e mamy nastpujce wartoci w tablicy Key:


Damian

Izabela

Rudolf

Waldemar

Key[1]

Key[2]

Key[3]

Key[4]

oraz
p1 =

3
8

p2 =

3
8

p3 =

1
8

p4 =

1
8

Tablice A oraz R utworzone przez algorytm 3.9 przedstawiono na rysunku 3.14,


natomiast drzewo utworzone przez algorytm 3.10 przedstawiono na rysunku 3.15.
Minimalny redni czas wyszukiwania wynosi 7/4.
Naley zauway, e R[1][2] moe by rwne 1 lub 2. Wynika to z faktu, e ktry z tych indeksw moe by indeksem korzenia w drzewie optymalnym, zawierajcym tylko pierwsze dwa klucze. Std oba te indeksy daj minimaln warto

Rozdzia 3.

Programowanie dynamiczne

145

Rysunek 3.14.
Tablice A oraz R utworzone
przez algorytm 3.9
w przypadku uycia go
wobec realizacji problemu
z przykadu 3.9

Rysunek 3.15.
Drzewo utworzone
w przypadku zastosowania
algorytmw 3.9 oraz 3.10
wobec realizacji problemu
z przykadu 3.9

A[1][2] w algorytmie 3.9, co oznacza, e dowolny z nich moe zosta wybrany


jako R[1][2].
Przedstawiony algorytm okrelania optymalnego drzewa wyszukiwania binarnego
pochodzi z pracy Gilberta i Moorea (1959). Algorytm (n2) mona otrzyma z metody przyspieszajcej programowania dynamicznego autorstwa Yao (1982).

3.6. Problem komiwojaera


Zamy, e komiwojaer planuje podr w interesach. Podr ta uwzgldnia odwiedzenie 20 miast. Kade miasto jest poczone z niektrymi innymi miastami za
pomoc drg. W celu zminimalizowania czasu podry chcemy okreli najkrtsz
tras, ktra rozpoczyna si w rodzinnym miecie komiwojaera, przebiega przez
wszystkie miasta raz i koczy si w punkcie startu. Problem okrelania najkrtszej trasy nosi nazw problemu komiwojaera.
Realizacja tego problemu moe by reprezentowana przez graf waony, w ktrym
kady wierzchoek reprezentuje miasto. Podobnie jak w podrozdziale 3.2 uoglniamy problem, tak aby uwzgldni przypadek, w ktrym waga (odlego) zwizana
z jednym kierunkiem poczenia dwch wierzchokw moe by rna ni w przypadku drugiego kierunku. Ponownie przyjmujemy, e wagi maj wartoci nieujemne.
Na rysunkach 3.2 oraz 3.16 przedstawiono takie grafy waone. Trasa (ang. tour),
okrelana rwnie mianem drogi Hamiltona (ang. Hamiltonian circuit), w grafie
skierowanym jest drog wiodc z wierzchoka do niego samego, przechodzc
przez wszystkie inne wierzchoki dokadnie raz. Optymalna trasa (ang. optimal

146

Podstawy algorytmw z przykadami w C++

tour) w waonym grafie skierowanym jest tak drog, ktra posiada najmniejsz
dugo. Problem komiwojaera polega na znalezieniu optymalnej trasy w waonym grafie skierowanym, kiedy istnieje przynajmniej jedna trasa. Ze wzgldu na
fakt, e wierzchoek pocztkowy nie ma wpywu na dugo optymalnej trasy,
jako wierzchoek pocztkowy bdziemy traktowa wierzchoek v1. Poniej przedstawiono trzy trasy i ich dugoci dla grafu z rysunku 3.16:
length[v1 , v 2 , v3 , v 4 , v1 ] = 22

length[v1 , v3 , v 2 , v 4 , v1 ] = 26
length[v1 , v3 , v 4 , v 2 , v1 ] = 21
Rysunek 3.16.
Optymalna trasa
ma posta:
[v1, v3, v4, v2, v1]

Ostatnia trasa jest optymalna. Realizacj problemu rozwizalimy, rozwaajc po


prostu wszystkie moliwe trasy. W oglnym przypadku moe istnie krawd czca
kady wierzchoek z kadym innym wierzchokiem. Jeeli rozwaymy wszystkie
moliwe trasy, drugi wierzchoek na trasie moe by jednym spord n1 wierzchokw, trzeci wierzchoek jednym spord n2 wierzchokw, , n-ty wierzchoek ostatnim wierzchokiem. Std cakowita liczba tras wynosi
(n 1)(n 2) L1 = (n 1)!

co oznacza warto gorsz od wykadniczej.


Pojawia si pytanie, czy mona do tego problemu zastosowa programowanie dynamiczne. Naley zauway, e jeeli vk jest pierwszym wierzchokiem po v1 na
trasie optymalnej, to droga podrzdna tej trasy, wiodca z vk do v1, musi by najkrtsz drog z vk do v1, ktra przechodzi przez wszystkie pozostae wierzchoki
dokadnie raz. Oznacza to, e zasada optymalnoci ma zastosowanie i moemy wykorzysta programowanie dynamiczne. W tym celu bdziemy reprezentowa graf
przez macierz przylegoci W, podobnie jak miao to miejsce w podrozdziale 3.2.
Na rysunku 3.17 przedstawiono macierz przylegoci, reprezentujc graf z rysunku 3.16. Niech
V = zbir wszystkich wierzchokw
A = podzbir zbioru V
D[vi][A] = dugo najkrtszej drogi z vi do v1 przechodzcej
przez kady wierzchoek w A dokadnie raz

Rozdzia 3.

Programowanie dynamiczne

147

Rysunek 3.17.
Macierz przylegoci W,
reprezentujca graf
z rysunku 3.16

Przykad 3.10.

Dla grafu z rysunku 3.16


V = {v1 , v 2 , v 3 , v 4 }

Naley zauway, e zapis {v1, v2, v3, v4} wykorzystuje nawiasy klamrowe w celu
reprezentowania zbioru, gdy zapis [v1, v2, v3, v4] wykorzystuje nawiasy kwadratowe w celu reprezentowania drogi. Jeeli A = {v3}, to
D[v 2 ][ A] = length[v 2 , v 3 , v1 ]
=

Jeeli A = {v3, v4}, to


D[v 2 ][ A] = minimum(length[v 2 , v3 , v 4 , v1 ], length[v 2 , v 4 , v3 , v1 ])
= minimum(20, ) = 20

Ze wzgldu na fakt, e zbir V{v1, vj} zawiera wszystkie wierzchoki oprcz v1 oraz
vj i ma tu zastosowanie zasada optymalnoci, moemy stwierdzi, co nastpuje:
Dlugo trasy minimalnej = minimum
1424
3 (W [1][ j ] + D[v j ][V {v1 , v j }])
2 j n

i oglnie dla i 1 oraz vi nie nalecego do A


D[vi ][ A] = minimum
1424
3 (W [i ][ j ] + D[v j ][ A {v j }]) jeeli A
j:v j A

(3.7)

D[vi ][] = W [i ][1]

Moemy utworzy algorytm programowania dynamicznego dla problemu komiwojaera, korzystajc z zalenoci 3.7. Jednak najpierw pokaemy, jak algorytm
ten dziaa.
Przykad 3.11.

Poniej okrelimy optymaln tras dla grafu reprezentowanego na rysunku 3.17.


Najpierw bierzemy pod uwag zbir pusty:
D[v 2 ][] = 1
D[v 3 ][] =
D[v 4 ][] = 6

148

Podstawy algorytmw z przykadami w C++

Nastpnie rozwaamy wszystkie zbiory, zawierajce jeden element:


D[v3 ][{v 2 }] = minimum(W [3][ j ] + D[v j ][{v 2 } {v j }])
= W [3][2] + D[v 2 ][] = 7 + 1 = 8

Podobnie:
D[v 4 ][{v 2 }] = 3 + 1 = 4
D[v 2 ][{v 3 }] = 6 + =
D[v 4 ][{v 3 }] = + =
D[v 2 ][{v 4 }] = 4 + 6 = 10
D[v 3 ][{v 4 }] = 8 + 6 = 14

Nastpnie rozwaamy wszystkie zbiory, zawierajce dwa elementy:


D[v4 ][{v2 , v3 }] = minimum
1424
3 (W [4][ j ] + D[v j ][{v2 , v3 } {v j }])
j:v j {v2 ,v3 }

= minimum(W [4][2] + D[v2 ][{v3 }],W [4][3] + D[v3 ][{v2 }])


= minimum(3 + , + 8) =

Podobnie:
D[v 3 ][{v 2 , v 4 }] = minimum(7 + 10, 8 + 4) = 12
D[v 2 ][{v 3 , v 4 }] = minimum(6 + 14, 4 + ) = 20

W kocu obliczamy dugo optymalnej trasy:


D[v1 ][{v2 , v3 , v4 }] = minimum
1424
3 (W [1][ j ] + D[v j ][{v2 , v3 , v4 } {v j }])
j:v j {v2 ,v3 ,v4 }

= minimum(W [1][2] + D[v2 ][{v3 , v4 }],


W [1][3] + D[v3 ][{v2 , v4 }],
W [1][4] + D[v4 ][{v2 , v3 }])
= minimum(2 + 20, 9 + 12, + ) = 21

Poniej przedstawiono algorytm programowania dynamicznego dla problemu


komiwojaera.
Algorytm 3.11.

Algorytm programowania dynamicznego dla problemu komiwojaera


Problem: okrel optymaln tras w waonym grafie skierowanym. Wagi s liczbami nieujemnymi.
Dane wejciowe: waony graf skierowany oraz n, liczba wierzchokw w grafie.

Graf jest reprezentowany przez dwuwymiarow tablic W, ktrej wiersze i kolumny


s indeksowane od 1 do n. W[i][j] jest wag krawdzi, prowadzcej od wierzchoka i-tego do j-tego.

Rozdzia 3.

Programowanie dynamiczne

149

Dane wyjciowe: zmienna minlength, ktrej wartoci jest dugo optymalnej trasy

oraz dwuwymiarowa tablica P, na podstawie ktrej mona skonstruowa optymaln tras. Wiersze tablicy P s indeksowane od 1 do n, za jej kolumny s indeksowane przez wszystkie podzbiory zbioru V{v1}. Element P[i][A] jest indeksem pierwszego wierzchoka, znajdujcego si po vi na najkrtszej drodze z vi do v1,
ktra przechodzi przez wszystkie wierzchoki nalece do A dokadnie raz.

 

  


  

  


  
     
  


 
   ! !"#$  $% &
 ! '  
 !( (!) ' 
 


 

  ! *+!!" ,(,,








 
! *+!!" ,(,,

  



Zanim pokaemy, w jaki sposb mona otrzyma optymaln tras na podstawie


tablicy P, przeanalizujemy algorytm. Po pierwsze, potrzebne jest nam twierdzenie.
Twierdzenie 3.1

Dla kadego n 1
n

k =1

k k = n2

n 1

Dowd. Jako wiczenie dla Czytelnika pozostawiono wykazanie, e:

n
n 1

k = n
k
k 1
Std:
n

k =1

n 1

k k = n k 1

n 1

= n
k =0 k
k =1

n 1

= n 2 n1

150

Podstawy algorytmw z przykadami w C++

Ostatnie rwnanie otrzymano dziki wykorzystaniu wyniku z przykadu A.10


z dodatku A.
Poniej przedstawiono analiz algorytmu 3.11.
Analiza
algorytmu
3.11.

Zoono czasowa i przestrzenna w kadym przypadku


(algorytm programowania dynamicznego dla problemu komiwojaera)
Operacja podstawowa: czas wykonania pierwszej i ostatniej ptli mona pomin
w porwnaniu z czasem wykonania ptli rodkowej, poniewa zawiera ona rne
poziomy zagniedenia. Std jako operacj podstawow bdziemy traktowa instrukcje wykonywane dla kadej wartoci vj. Naley do nich instrukcja dodawania.
Rozmiar danych wejciowych: n, liczba wierzchokw w grafie.

Dla kadego zbioru A zawierajcego k wierzchokw musimy rozpatrzy n1k


wierzchokw, a dla kadego z tych wierzchokw operacja podstawowa jest wykonywana k razy. Liczba podzbiorw A zbioru V{v1}, zawierajcych k wierzchokw, wynosi nk1 , wic cakowit liczb powtrze operacji podstawowej okrela

( )

zaleno:
n2
n 1

T (n) = (n 1 k )k
k =1
k

(3.8)

Nietrudno wykaza, e:

n 1
n 2
= (n 1)

(n 1 k )
k
k
Podstawiajc to rwnanie do rwnania 3.8 otrzymujemy:
n2
n 2

T (n) = (n 1) k
k
k =1

Ostatecznie, stosujc twierdzenie 3.1, otrzymujemy:

T (n) = (n 1)(n 2)2 n 3 (n 2 2 n )


Ze wzgldu na fakt, e algorytm uywa rwnie duej iloci pamici, przeanalizujemy take jego zoono pamiciow, ktr okrelimy symbolem M(n). Pami
uyta do przechowania tablic D[vi][A] oraz P[vi][A] stanowi oczywicie gwny
skadnik poziomu zajtoci pamici. Okrelimy wic rozmiar tych tablic. Ze wzgldu
na fakt, e zbir V{v1} zawiera n1 wierzchokw, moemy zastosowa wyniki
otrzymane w przykadzie A.10 w dodatku A, stwierdzajc, e posiada on 2n1
podzbiorw A. Pierwszy indeks tablic D i P naley do zakresu od 1 do n. Std:

M (n) = 2 n2 n 1 = n2 n (n2 n )

Rozdzia 3.

Programowanie dynamiczne

151

W tym momencie Czytelnik moe si zastanawia, co osignlimy, skoro nasz


algorytm jest rzdu (n22n). Poniszy przykad pokazuje jednak, e nawet algorytm
o zoonoci na takim poziomie moe by niekiedy przydatny.
Przykad 3.12.

Rudolf i Natalia wspzawodnicz o t sam posad sprzedawcy. Szef powiedzia im


w pitek, e ta osoba, ktra od poniedziaku szybciej odbdzie podr po caym
rewirze, na ktrego obszarze ley 20 miast, zdobdzie posad. Na obszarze rewiru znajduje si biuro gwne, do ktrego naley powrci po odwiedzeniu innych
miejscowoci. Z kadego miasta istnieje droga do kadego innego miasta. Rudolf
stwierdzi, e ma cay weekend na opracowanie trasy, wic postanowi po prostu
uruchomi na swoim komputerze algorytm siowy, ktry rozpatrzyby wszystkie
(201)! tras. Natalia przypomniaa sobie algorytm programowania dynamicznego, ktry poznaa na zajciach z algorytmiki. Stwierdzajc, e musi wykorzysta
kad nadarzajc si okazj, uruchomia ten algorytm na swoim komputerze.
Zakadajc, e czas przetworzenia operacji podstawowej przez algorytm Natalii
wynosi 1 mikrosekund oraz e ten sam czas zajmuje algorytmowi Rudolfa obliczenie dugoci jednej trasy, czas wykonania kadego z algorytmw wynosi:
Algorytm siowy: 19! s = 3857 lat.
Algorytm programowania dynamicznego: (201)(202)2203 s = 45 sekund.
Jak wida, nawet algorytm rzdu (n22n) moe okaza si przydatny w sytuacji,
gdy alternatyw jest algorytm rzdu silnia. Pami uywana przez algorytm programowania dynamicznego w tym przykadzie ma rozmiar

20 2 20 = 20 971 520 elementw macierzy.


Cho jest to do dua liczba, z pewnoci nie przekracza wartoci oferowanej przez
wspczesne standardy.
Uycie algorytmu rzdu (n22n) w celu znalezienia optymalnej trasy jest praktyczne jedynie dla maych wartoci n. Gdyby na przykad chodzio o 60 miast,
wykonanie algorytmu zajoby wiele lat.
Poniej omwimy sposb okrelania optymalnej trasy na podstawie tablicy P. Nie
podamy algorytmu, a jedynie zilustrujemy sposb postpowania. Elementy tablicy P, wymagane do okrelenia optymalnej trasy dla grafu reprezentowanego na
rysunku 3.16, to:

P[1, {v 2 , v3 , v 4 }] P[3, {v 2 , v 4 }] P[4, {v 2 }]


Optymaln tras otrzymujemy w nastpujcy sposb:

Indeks pierwszego wierzchoka = P[1][{v2 , v3 , v4 }] = 3


Indeks drugiego wierzchoka = P[3][{v2 , v4 }] = 4
Indeks trzeciego wierzchoka = P[4][{v2 }] = 2

152

Podstawy algorytmw z przykadami w C++

Optymalna trasa ma zatem posta:

[v1 , v 3 , v 4 , v 2 , v1 ]
Jak dotd nikomu nie udao si opracowa takiego algorytmu dla problemu komiwojaera, ktrego zoono w najgorszym przypadku byaby lepsza ni wykadnicza. Jednake nikt rwnie nie udowodni, e taki algorytm nie istnieje. Problem ten naley do szerokiej klasy blisko ze sob zwizanych problemw, ktre
dziel t waciwo. S one tematem rozdziau 9.

wiczenia
Podrozdzia 3.1
1. Wyprowad rwnanie 3.1, podane w tym podrozdziale.
2. Uyj dowodu indukcyjnego wzgldem n w celu wykazania, e algorytm typu

dziel i zwyciaj dla wspczynnika dwumianowego (algorytm 3.1), oparty na


n
n
rwnaniu 3.1, oblicza 2 1 skadnikw w celu okrelenia wartoci .
k
k
3. Zaimplementuj oba algorytmy rozwizania problemu wspczynnika
dwumianowego (algorytm 3.1 oraz 3.2) w swoim systemie i przeanalizuj
ich wydajno przy uyciu rnych realizacji problemu.
4. Zmodyfikuj algorytm 3.2 (wspczynnik dwumianowy przy uyciu

programowania dynamicznego), tak aby wykorzystywa tylko tablic


jednowymiarow, indeksowan od 0 do k.

Podrozdzia 3.2
5. Uyj algorytmu Floyda dla problemu najkrtszych drg 2 (algorytm 3.4)

w celu skonstruowania macierzy D, ktra bdzie zawiera dugoci


najkrtszych drg, oraz macierzy P, ktra bdzie zawiera najwysze
indeksy wierzchokw porednich, nalecych do najkrtszych drg
dla poniszego grafu. Przedstaw wykonywane dziaania krok po kroku.

Rozdzia 3.

Programowanie dynamiczne

153

6. Uyj algorytmu wywietlania najkrtszej drogi (algorytm 3.5) w celu

znalezienia najkrtszej drogi z wierzchoka v7 do wierzchoka v3


w grafie z wiczenia 5, wykorzystujc macierz P, okrelon w tym
wiczeniu. Przedstaw wykonywane dziaania krok po kroku.
7. Przeanalizuj algorytm wywietlania najkrtszej drogi (algorytm 3.5)

i wyka, e charakteryzuje go liniowa zoono czasowa.


8. Zaimplementuj algorytm Floyda dla problemu najkrtszych drg

2 (algorytm 3.4) w swoim systemie i przeanalizuj jego wydajno,


uywajc rnych grafw.
9. Czy algorytm Floyda dla problemu najkrtszych drg 2 (algorytm 3.4)

mona tak zmodyfikowa, aby dawa w wyniku najkrtsz drog z danego


wierzchoka do innego okrelonego wierzchoka w grafie? Uzasadnij
swoj odpowied.
10. Czy algorytm Floyda dla problemu najkrtszych drg 2 (algorytm 3.4)

moe by uywany do znajdowania najkrtszych drg w grafie


zawierajcym ujemne wartoci wag? Uzasadnij swoj odpowied.

Podrozdzia 3.3
11. Znajd problem optymalizacji, w przypadku ktrego zasada optymalnoci

nie ma zastosowania, a std rozwizanie optymalne nie moe zosta


otrzymane przy uyciu programowania dynamicznego. Uzasadnij swoj
odpowied.

Podrozdzia 3.4
12. Znajd optymaln kolejno oraz koszt obliczenia iloczynu macierzy

A1A2A3A4A5, gdzie
A1 ma rozmiar (10 4)
A2 ma rozmiar (4 5)
A3 ma rozmiar (5 20)
A4 ma rozmiar (20 2)
A5 ma rozmiar (2 50)
13. Zaimplementuj algorytm minimalnej liczby operacji mnoenia (algorytm 3.6)

oraz algorytm wywietlania optymalnej kolejnoci (algorytm 3.7) w swoim


systemie i przeanalizuj ich wydajno, uywajc rnych realizacji problemu.
14. Poka, e algorytm typu dziel i zwyciaj, oparty na rwnaniu 3.5,

charakteryzuje wykadnicza zoono czasowa.


15. Wyprowad rwnanie:
n 1

[(n diagonal ) diagonal ] =

diagonal =1

n(n 1)(n + 1)
6

154

Podstawy algorytmw z przykadami w C++

Jest ono wykorzystywane w analizie zoonoci czasowej w kadym


przypadku algorytmu 3.6.
16. Poka, e w celu penego opatrzenia nawiasami wyraenia zawierajcego

n macierzy potrzebnych jest n1 par nawiasw.


17. Przeanalizuj algorytm 3.7 i poka, e charakteryzuje go liniowa zoono

czasowa.
18. Zapisz wydajny algorytm, ktry znajduje optymaln kolejno mnoenia

n macierzy A1A2An, gdzie rozmiary kadej macierzy wynosz 11,


1d, d1 lub dd dla pewnej dodatniej liczby cakowitej d. Przeanalizuj
swj algorytm i przedstaw wyniki, uywajc notacji rzdu.

Podrozdzia 3.5
19. Ile rnych drzewa wyszukiwania binarnego mona skonstruowa

przy uyciu szeciu rnych kluczy?


20. Utwrz optymalne drzewo wyszukiwania binarnego dla nastpujcych

elementw, ktrych prawdopodobiestwa wystpienia podano w nawiasach:


CASE (0,05), ELSE (0,15), END (0,05), IF (0,35), OF (0,05), THEN (0,35).
j

21. Znajd wydajny sposb obliczania sumy

p
m =i

, ktra jest uywana

w algorytmie optymalnego drzewa wyszukiwania binarnego (algorytm 3.9).


22. Zaimplementuj algorytm optymalnego drzewa wyszukiwania binarnego

(algorytm 3.9) oraz algorytm budowania optymalnego drzewa wyszukiwania


binarnego (algorytm 3.10) w swoim systemie i przeanalizuj ich wydajno,
uywajc rnych realizacji problemu.
23. Przeanalizuj algorytm 3.10 i przedstaw jego zoono czasow przy uyciu

notacji rzdu.
24. Uoglnij algorytm wyszukiwania optymalnego drzewa binarnego

(algorytm 3.9), tak aby uwzgldni przypadek, w ktrym klucz wyszukiwania


moe nie wystpowa w drzewie. Oznacza to, e naley przyj qi, gdzie
i = 0, 1, 2, , n i jest prawdopodobiestwem tego, e brakujcy klucz
mona umiejscowi midzy kluczami Keyi a Keyi+1. Przeanalizuj swoj
uoglnion wersj algorytmu i przedstaw wyniki, uywajc notacji rzdu.
25. Poka, e algorytm typu dziel i zwyciaj oparty na rwnaniu 3.6

charakteryzuje wykadnicza zoono czasowa.

Podrozdzia 3.6
26. Znajd optymaln tras dla waonego grafu skierowanego, reprezentowanego

przez ponisz macierz W. Przedstaw wykonywane dziaania krok po kroku.

Rozdzia 3.

Programowanie dynamiczne

155

0 8 13 18 20
3 0 7 8 10

W = 4 11 0 10 7

6 6 7 0 11
10 6 2 1 0
27. Zapisz bardziej szczegow wersj algorytmu programowania

dynamicznego dla problemu komiwojaera (algorytm 3.11).


28. Zaimplementuj swoj bardziej szczegow wersj algorytmu 3.11

z wiczenia 27 w swoim systemie i przeanalizuj jego wydajno,


uywajc kilku realizacji problemu.

wiczenia dodatkowe
29. Podobnie jak w przypadku algorytmw obliczajcych n-ty wyraz cigu

Fibonacciego (patrz wiczenie 34 w rozdziale 1.) rozmiar danych wejciowych


algorytmu 3.2 (wspczynnik dwumianowy przy uyciu programowania
dynamicznego) jest liczb symboli, uytych do zakodowania liczb n i k.
Przeanalizuj ten algorytm pod wzgldem rozmiaru jego danych wejciowych.
30. Okrel liczb moliwych kolejnoci mnoenia n macierzy A1A2An.
31. Poka, e liczb drzew wyszukiwania binarnego o n kluczach okrela wzr:

1 2n

n + 1 n
32. Czy mona opracowa algorytm optymalnego drzewa wyszukiwania

binarnego (algorytm 3.9), wykonywany w czasie kwadratowym?


33. Wykorzystaj programowanie dynamiczne w celu zapisania algorytmu

znajdujcego maksymaln sum w dowolnej cigej licie podrzdnej


danej listy n liczb rzeczywistych. Przeanalizuj swj algorytm i przedstaw
rezultaty, uywajc notacji rzdu.
34. Rozwamy dwa cigi znakw: S1 oraz S2. Przykadowo, mog one mie

posta S1 = A$CMA*MN oraz S2 = AXMC4ANB. Zakadajc, e cig


podrzdny cigu moe zosta skonstruowany poprzez usunicie dowolnej
liczby znakw z dowolnych pozycji, wykorzystaj programowanie
dynamiczne w celu utworzenia algorytmu, znajdujcego najduszy
wsplny cig podrzdny cigw S1 i S2. Algorytm ten zwraca wsplny
cig podrzdny o maksymalnej dugoci kadego z cigw.

You might also like