informatyka algorytmy struktury danych i techniki programowania wydanie iv piotr wroblewski ebook

background image

Algorytmy, struktury danych
i techniki programowania.
Wydanie IV

Autor:

Piotr Wróblewski

ISBN: 978-83-246-2306-8
Format: 158

u235, stron: 452

Podstawowy podrêcznik do nauki algorytmiki

• Przystêpne wprowadzenie do algorytmiki
• Bez zbêdnej teorii
• Gotowe rozwi¹zania w C++

Oto kolejne wydanie sprawdzonej i cenionej przez programistów, wyk³adowców
oraz studentów ksi¹¿ki, bêd¹cej podstawowym podrêcznikiem do nauki algorytmiki.
W pierwszej kolejnoœci autor zapozna Ciê z elementarnymi zagadnieniami z tej
dziedziny oraz wyjaœni, sk¹d bierze siê tak szybki postêp w tej dyscyplinie nauki.
Podczas dalszej lektury poznasz takie pojêcia, jak rekurencja, analiza z³o¿onoœci oraz
algorytmy sortowania i przeszukiwania czy algorytmy numeryczne. Szybko opanujesz
metody optymalizacji algorytmów, sposoby kodowania i kompresji danych oraz elementy
algorytmiki grafów. Przedstawione w ksi¹¿ce algorytmy zilustrowane zosta³y przyk³adowymi
kodami Ÿród³owymi w C++ , u³atwiaj¹cymi zrozumienie poznawanych zagadnieñ.
Przejrzysta forma, praktyczne przyk³ady oraz przystêpny jêzyk sprawiaj¹, ¿e ksi¹¿ka
ta pozwala szybko, a tak¿e bezboleœnie opanowaæ zarówno algorytmy, jak i struktury
danych oraz najlepsze techniki programowania.

• Historia algorytmiki
• Wykorzystanie rekurencji
• Analiza z³o¿onoœci algorytmów
• Algorytmy sortowania
• Algorytmy przeszukiwania
• Przeszukiwanie tekstów
• Struktury danych i ich implementacja
• Optymalizacja algorytmów
• Zaawansowane techniki programowania
• Wykorzystanie grafów
• Wprowadzenie do sztucznej inteligencji
• Kodowanie i kompresja danych
• Algorytmy numeryczne
• Poradnik kompilacji i uruchamiania programów (GCC, DevC++, Microsoft Visual

C++ Express Edition).

Szybko i bezboleœnie opanuj wszystkie zagadnienia algorytmiki!

background image

Spis tre

Ĉci

Przedmowa ...................................................................................... 9

Rozdzia

ä 1. Zanim wystartujemy ....................................................................... 17

Jak to wcze

Ğniej bywaáo, czyli wyjątki z historii maszyn algorytmicznych ................... 18

Jak to si

Ċ niedawno odbyáo,

czyli o tym, kto „wymy

Ğliá” metodologiĊ programowania ........................................... 21

Proces koncepcji programów .......................................................................................... 22
Poziomy abstrakcji opisu i wybór j

Ċzyka ....................................................................... 23

Poprawno

Ğü algorytmów ................................................................................................ 24

Rozdzia

ä 2. Rekurencja .................................................................................... 27

Definicja rekurencji ........................................................................................................ 27
Ilustracja poj

Ċcia rekurencji ............................................................................................ 28

Jak wykonuj

ą siĊ programy rekurencyjne? ..................................................................... 30

Niebezpiecze

Ĕstwa rekurencji ........................................................................................ 31

Ci

ąg Fibonacciego .................................................................................................... 31

Stack overflow! ........................................................................................................ 33

Pu

áapek ciąg dalszy ........................................................................................................ 34

St

ąd do wiecznoĞci ................................................................................................... 34

Definicja poprawna, ale... ......................................................................................... 35

Typy programów rekurencyjnych ................................................................................... 36
My

Ğlenie rekurencyjne ................................................................................................... 38

Przyk

áad 1.: Spirala .................................................................................................. 38

Przyk

áad 2.: Kwadraty „parzyste” ............................................................................ 40

Uwagi praktyczne na temat technik rekurencyjnych ...................................................... 41
Zadania ........................................................................................................................... 42
Rozwi

ązania i wskazówki do zadaĔ ............................................................................... 44

Rozdzia

ä 3. Analiza zäoĔonoĈci algorytmów ........................................................ 49

Definicje i przyk

áady ...................................................................................................... 50

Jeszcze raz funkcja silnia ......................................................................................... 53
Zerowanie fragmentu tablicy .................................................................................... 57
Wpadamy w pu

áapkĊ ................................................................................................ 58

Īne typy záoĪonoĞci obliczeniowej ...................................................................... 59

Nowe zadanie: upro

Ğciü obliczenia! ............................................................................... 61

background image

4

Algorytmy, struktury danych i techniki programowania

Analiza programów rekurencyjnych ............................................................................... 62

Terminologia i definicje ........................................................................................... 62
Ilustracja metody na przyk

áadzie .............................................................................. 63

Rozk

áad logarytmiczny ............................................................................................. 64

Zamiana dziedziny równania rekurencyjnego .......................................................... 66
Funkcja Ackermanna, czyli co

Ğ dla smakoszy ......................................................... 66

Z

áoĪonoĞü obliczeniowa to nie religia! ........................................................................... 68

Techniki optymalizacji programów ................................................................................ 68
Zadania ........................................................................................................................... 69
Rozwi

ązania i wskazówki do zadaĔ ............................................................................... 70

Rozdzia

ä 4. Algorytmy sortowania ..................................................................... 73

Sortowanie przez wstawianie, algorytm klasy O(N

2

) ..................................................... 74

Sortowanie b

ąbelkowe, algorytm klasy O(N

2

) ............................................................... 75

Quicksort, algorytm klasy O(N log N) ........................................................................... 77
Heap Sort — sortowanie przez kopcowanie ................................................................... 80
Scalanie zbiorów posortowanych ................................................................................... 82
Sortowanie przez scalanie ............................................................................................... 83
Sortowanie zewn

Ċtrzne ................................................................................................... 84

Uwagi praktyczne ........................................................................................................... 87

Rozdzia

ä 5. Typy i struktury danych .................................................................. 89

Typy podstawowe i z

áoĪone ........................................................................................... 89

Ci

ągi znaków i napisy w C++ ......................................................................................... 90

Abstrakcyjne struktury danych ....................................................................................... 92

Listy jednokierunkowe ............................................................................................. 93
Tablicowa implementacja list ................................................................................. 115
Stos ......................................................................................................................... 119
Kolejki FIFO .......................................................................................................... 123
Sterty i kolejki priorytetowe ................................................................................... 125
Drzewa i ich reprezentacje ..................................................................................... 131
Zbiory ..................................................................................................................... 143
Zadania ................................................................................................................... 145
Rozwi

ązania zadaĔ ................................................................................................. 146

Rozdzia

ä 6. Derekursywacja i optymalizacja algorytmów .................................. 147

Jak pracuje kompilator? ................................................................................................ 148
Odrobina formalizmu nie zaszkodzi! ............................................................................ 150
Kilka przyk

áadów derekursywacji algorytmów ............................................................ 151

Derekursywacja z wykorzystaniem stosu ..................................................................... 154

Eliminacja zmiennych lokalnych ............................................................................ 154

Metoda funkcji przeciwnych ........................................................................................ 156
Klasyczne schematy derekursywacji ............................................................................ 158

Schemat typu while ................................................................................................ 159
Schemat typu if-else ............................................................................................... 160
Schemat z podwójnym wywo

áaniem rekurencyjnym ............................................. 162

Podsumowanie .............................................................................................................. 163

Rozdzia

ä 7. Algorytmy przeszukiwania ............................................................. 165

Przeszukiwanie liniowe ................................................................................................ 165
Przeszukiwanie binarne ................................................................................................ 166
Transformacja kluczowa (hashing) ............................................................................... 167

W poszukiwaniu funkcji H ..................................................................................... 169
Najbardziej znane funkcje H .................................................................................. 169
Obs

áuga konfliktów dostĊpu ................................................................................... 171

background image

Spis tre

Ĉci

5

Powrót do

Ĩródeá .................................................................................................... 172

Jeszcze raz tablice! ................................................................................................. 173
Próbkowanie liniowe .............................................................................................. 173
Podwójne kluczowanie ........................................................................................... 175
Zastosowania transformacji kluczowej ................................................................... 176
Podsumowanie metod transformacji kluczowej ..................................................... 176

Rozdzia

ä 8. Przeszukiwanie tekstów ............................................................... 179

Algorytm typu brute-force ............................................................................................ 179
Nowe algorytmy poszukiwa

Ĕ ....................................................................................... 181

Algorytm K-M-P .................................................................................................... 182
Algorytm Boyera i Moore’a ................................................................................... 185
Algorytm Rabina i Karpa ....................................................................................... 187

Rozdzia

ä 9. Zaawansowane techniki programowania ....................................... 191

Programowanie typu „dziel i zwyci

ĊĪaj” ...................................................................... 192

Odszukiwanie minimum i maksimum w tablicy liczb ............................................ 193
Mno

Īenie macierzy o rozmiarze N×N .................................................................... 195

Mno

Īenie liczb caákowitych ................................................................................... 197

Inne znane algorytmy „dziel i zwyci

ĊĪaj” .............................................................. 198

Algorytmy „

Īaráoczne”, czyli przekąsiü coĞ nadszedá juĪ czas... .................................. 199

Problem plecakowy, czyli nie

áatwe jest Īycie turysty piechura .............................. 200

Programowanie dynamiczne ......................................................................................... 202

Ci

ąg Fibonacciego .................................................................................................. 203

Równania z wieloma zmiennymi ........................................................................... 204
Najd

áuĪsza wspólna podsekwencja ........................................................................ 205

Inne techniki programowania ....................................................................................... 208
Uwagi bibliograficzne .................................................................................................. 210

Rozdzia

ä 10. Elementy algorytmiki grafów ......................................................... 211

Definicje i poj

Ċcia podstawowe .................................................................................... 212

Cykle w grafach ............................................................................................................ 214
Sposoby reprezentacji grafów ....................................................................................... 217

Reprezentacja tablicowa ......................................................................................... 217
S

áowniki wĊzáów .................................................................................................... 218

Listy kontra zbiory ................................................................................................. 219

Podstawowe operacje na grafach .................................................................................. 220

Suma grafów .......................................................................................................... 220
Kompozycja grafów ............................................................................................... 220
Pot

Ċga grafu ........................................................................................................... 220

Algorytm Roy-Warshalla ............................................................................................. 221
Algorytm Floyda-Warshalla ......................................................................................... 224
Algorytm Dijkstry ........................................................................................................ 227
Algorytm Bellmana-Forda ............................................................................................ 228
Drzewo rozpinaj

ące minimalne .................................................................................... 228

Algorytm Kruskala ................................................................................................. 229
Algorytm Prima ...................................................................................................... 230

Przeszukiwanie grafów ................................................................................................. 230

Strategia „w g

áąb” (przeszukiwanie zstĊpujące) ..................................................... 231

Strategia „wszerz” .................................................................................................. 232
Inne strategie przeszukiwania ................................................................................. 234

Problem w

áaĞciwego doboru ......................................................................................... 235

Podsumowanie .............................................................................................................. 239
Zadania ......................................................................................................................... 239

background image

6

Algorytmy, struktury danych i techniki programowania

Rozdzia

ä 11. Algorytmy numeryczne ................................................................. 241

Poszukiwanie miejsc zerowych funkcji ........................................................................ 241
Iteracyjne obliczanie warto

Ğci funkcji .......................................................................... 243

Interpolacja funkcji metod

ą Lagrange’a ....................................................................... 244

Īniczkowanie funkcji ............................................................................................... 245

Ca

ákowanie funkcji metodą Simpsona .......................................................................... 247

Rozwi

ązywanie ukáadów równaĔ liniowych metodą Gaussa ....................................... 248

Uwagi ko

Ĕcowe ............................................................................................................ 251

Rozdzia

ä 12. Czy komputery mogñ myĈleè? ....................................................... 253

Przegl

ąd obszarów zainteresowaĔ Sztucznej Inteligencji ............................................. 254

Systemy eksperckie ................................................................................................ 255
Sieci neuronowe ..................................................................................................... 256

Reprezentacja problemów ............................................................................................ 257

ûwiczenie 1. ........................................................................................................... 258

Gry dwuosobowe i drzewa gier .................................................................................... 259
Algorytm mini-max ...................................................................................................... 260

Rozdzia

ä 13. Kodowanie i kompresja danych .................................................... 265

Kodowanie danych i arytmetyka du

Īych liczb ............................................................. 267

Kodowanie symetryczne ........................................................................................ 267
Kodowanie asymetryczne ....................................................................................... 268
Metody prymitywne ............................................................................................... 274

àamanie szyfrów .................................................................................................... 275

Techniki kompresji danych ........................................................................................... 275

Kompresja poprzez modelowanie matematyczne ................................................... 277
Kompresja metod

ą RLE ......................................................................................... 278

Kompresja danych metod

ą Huffmana .................................................................... 279

Kodowanie LZW .................................................................................................... 283
Idea kodowania s

áownikowego na przykáadach ..................................................... 284

Opis formatu GIF ................................................................................................... 286

Rozdzia

ä 14. Zadania róĔne .............................................................................. 289

Teksty zada

Ĕ ................................................................................................................. 289

Rozwi

ązania ................................................................................................................. 291

Dodatek A Poznaj C++ w pi

öè minut! ............................................................. 295

Elementy j

Ċzyka C++ na przykáadach .......................................................................... 295

Pierwszy program ................................................................................................... 295
Dyrektywa #include ............................................................................................... 296

Podprogramy ................................................................................................................ 296

Procedury ............................................................................................................... 296
Funkcje ................................................................................................................... 297

Operacje arytmetyczne ................................................................................................. 298

Operacje logiczne ................................................................................................... 298
Wska

Ĩniki i zmienne dynamiczne .......................................................................... 299

Referencje ..................................................................................................................... 300
Typy z

áoĪone ................................................................................................................ 300

Tablice .................................................................................................................... 300
Rekordy .................................................................................................................. 301
Instrukcja switch .................................................................................................... 301

Iteracje .......................................................................................................................... 302
Struktury rekurencyjne ................................................................................................. 303
Parametry programu main() .......................................................................................... 303
Operacje na plikach w C++ .......................................................................................... 303

background image

Spis tre

Ĉci

7

Programowanie obiektowe w C++ ............................................................................... 304

Terminologia .......................................................................................................... 304
Obiekty na przyk

áadzie ........................................................................................... 305

Sk

áadowe statyczne klas ......................................................................................... 308

Metody sta

áe klas .................................................................................................... 308

Dziedziczenie w

áasnoĞci ......................................................................................... 308

Kod warunkowy w C++ ............................................................................................... 311

Dodatek B Systemy obliczeniowe w pigu

äce ................................................... 313

Kilka definicji ............................................................................................................... 313
System dwójkowy ........................................................................................................ 313

Operacje arytmetyczne na liczbach dwójkowych ................................................... 315
Operacje logiczne na liczbach dwójkowych ........................................................... 315

System ósemkowy ........................................................................................................ 317
System szesnastkowy ................................................................................................... 317
Zmienne w pami

Ċci komputera .................................................................................... 317

Kodowanie znaków ...................................................................................................... 318

Dodatek C Kompilowanie programów przyk

äadowych ...................................... 321

Zawarto

Ğü archiwum ZIP na ftp .................................................................................... 321

Darmowe kompilatory C++ .......................................................................................... 321
Kompilacja i uruchamianie ........................................................................................... 322

GCC ....................................................................................................................... 322
Visual C++ Express Edition ................................................................................... 323
Dev C++ ................................................................................................................. 327

Literatura ..................................................................................... 329

Spis tabel ................................................................................... 331

Spis ilustracji .............................................................................. 333

Skorowidz ................................................................................... 339

background image

Rozdzia

ä 8.

Przeszukiwanie tekstów

Zanim na dobre zanurzymy si

Ċ w lekturĊ nowego rozdziaáu, naleĪy wyjaĞniü pewne nieporozumie-

nie, które mo

Īe towarzyszyü jego tytuáowi. OtóĪ za tekst bĊdziemy uwaĪali ciąg znaków w sen-

sie informatycznym. Nie zawsze b

Ċdzie to miaáo cokolwiek wspólnego z ludzką „pisaniną”! Tek-

stem b

Ċdzie na przykáad równieĪ ciąg bitów

1

, który tylko przez umowno

Ğü moĪe byü podzielony

na równej wielko

Ğci porcje, którym przyporządkowano pewien kod liczbowy

2

.

Okazuje si

Ċ wszelako, Īe przyjĊcie konwencji dotyczących interpretacji informacji uáatwia wiele

operacji na niej. Dlatego te

Ī pozostaĔmy przy ogólnikowym stwierdzeniu „tekst”, wiedząc, Īe za

tym okre

Ğleniem moĪe siĊ kryü doĞü sporo znaczeĔ.

Algorytm typu brute-force

Zadaniem, które b

Ċdziemy usiáowali wspólnie rozwiązaü, jest poszukiwanie wzorca

3

w o d

áugoĞci

M znaków w tek

Ğcie t o dáugoĞci N. Z áatwoĞcią moĪemy zaproponowaü doĞü oczywisty algorytm

rozwi

ązujący to zadanie, bazując na pomysáach symbolicznie przedstawionych na rysunku 8.1.

Rysunek 8.1.
Algorytm typu
brute-force
przeszukiwania tekstu

Zarezerwujmy indeksy

j

i

i

do poruszania si

Ċ odpowiednio we wzorcu i tekĞcie podczas opera-

cji porównywania znak po znaku zgodno

Ğci wzorca z tekstem. ZaáóĪmy, Īe w trakcie poszuki-

wa

Ĕ obszary objĊte szarym kolorem na rysunku okazaáy siĊ zgodne. Po stwierdzeniu tego faktu

przesuwamy si

Ċ zarówno we wzorcu, jak i w tekĞcie o jedną pozycjĊ do przodu (

i++; j++)

.

Ī siĊ jednak powinno staü z indeksami

i

oraz

j

podczas stwierdzenia niezgodno

Ğci znaków?

W takiej sytuacji ca

áe poszukiwanie koĔczy siĊ poraĪką, co zmusza nas do anulowania „szarej

strefy” zgodno

Ğci. Czynimy to poprzez cofniĊcie siĊ w tekĞcie o to, co byáo zgodne, czyli o

j-1

1

Reprezentuj

ący np. pamiĊü ekranu.

2

Np. ASCII lub dowolny inny.

3

Ang. pattern matching.

background image

180

Algorytmy, struktury danych i techniki programowania

znaków, wyzerowuj

ąc przy okazji

j

. Omówmy jeszcze moment stwierdzenia ca

ákowitej zgodnoĞci

wzorca z tekstem. Kiedy to nast

ąpi? OtóĪ nietrudno zauwaĪyü, Īe podczas stwierdzenia zgodnoĞci

ostatniego znaku

j

powinno zrówna

ü siĊ z

M

. Mo

Īemy wówczas áatwo odtworzyü pozycjĊ, od

której wzorzec startuje w badanym tek

Ğcie: bĊdzie to oczywiĞcie

i-M

.

T

áumacząc powyĪsze sytuacje na C++, moĪemy áatwo dojĞü do nastĊpującej procedury:

txt-1.cpp

int

szukaj(char *w, char *t)

{
int

i=0,j=0, M=strlen(w), N=strlen(t);

while

( (j<M) && (i<N) )

{
if(t[i]!=w[j])
{
i-=j-1;
j=-1;
}
i++;
j++;
}
if

(j==M)

return i-M;
else
return -1;
}

Sposób korzystania z funkcji

szukaj

jest przedstawiony na przyk

áadzie nastĊpującej funkcji

main

:

int

main()

{
char

*b="abrakadabra",*a="rak";

cout << szukaj(a,b) << endl;
}

Jako wynik funkcji zwracana jest pozycja w tek

Ğcie, od której zaczyna siĊ wzorzec, lub

-1

w przypadku, gdy poszukiwany tekst nie zosta

á odnaleziony — jest to znana nam juĪ dosko-

nale konwencja. Przypatrzmy si

Ċ dokáadniej przykáadowi poszukiwania wzorca

10100

w pewnym

tek

Ğcie binarnym (patrz rysunek 8.2).

Rysunek 8.2.
„Fa

ászywe starty”

podczas poszukiwania

background image

Rozdzia

ä 8.

i Przeszukiwanie tekstów

181

Rysunek jest nieco uproszczony: w istocie poziome przesuwanie si

Ċ wzorca oznacza instrukcje

zaznaczone na listingu jako (*), natomiast ca

áa szara strefa o dáugoĞci k oznacza k-krotne wyko-

nanie (**).

Na podstawie zobrazowanego przyk

áadu moĪemy spróbowaü wymyĞliü taki najgorszy tekst i wzorzec,

dla których proces poszukiwania b

Ċdzie trwaá moĪliwie najdáuĪej. Chodzi oczywiĞcie zarówno

o tekst, jak i wzorzec z

áoĪone z samych zer i zakoĔczone jedynką

4

.

Spróbujmy obliczy

ü klasĊ tego algorytmu dla opisanego przed chwilą ekstremalnego najgor-

szego przypadku. Obliczenie nie nale

Īy do skomplikowanych czynnoĞci: zakáadając, Īe restart al-

gorytmu b

Ċdzie konieczny

(N-1)-(M-2)=N-M+1

razy, i wiedz

ąc, Īe podczas kaĪdego cyklu jest ko-

nieczne wykonanie

M

porówna

Ĕ, otrzymujemy natychmiast

M(N-M+1)

, czyli oko

áo

5

M

˜N

.

Zaprezentowany w tym paragrafie algorytm wykorzystuje komputer jako bezmy

Ğlne, ale

sprawne liczyd

áo

6

. Jego z

áoĪonoĞü obliczeniowa eliminuje go w praktyce z przeszukiwania tek-

stów binarnych, w których mo

Īe wystąpiü wiele niekorzystnych konfiguracji danych. Jedyną zaletą

algorytmu jest jego prostota, co i tak nie czyni go na tyle atrakcyjnym, by da

ü siĊ zamĊczyü jego

powolnym dzia

áaniem.

Nowe algorytmy poszukiwa

þ

Algorytm, o którym b

Ċdzie mowa w tym rozdziale, posiada ciekawą historiĊ, którą w formie

anegdoty warto przytoczy

ü. OtóĪ w 1970 roku S. A. Cook udowodniá teoretyczny rezultat doty-

cz

ący pewnej abstrakcyjnej maszyny. Wynikaáo z niego, Īe istniaá algorytm poszukiwania

wzorca w tek

Ğcie, który dziaáaá w czasie proporcjonalnym do

M+N

w najgorszym przypadku.

Rezultat pracy Cooka wcale nie by

á przewidziany do praktycznych celów, niemniej D. E.

Knuth i V. R. Pratt otrzymali na jego podstawie algorytm, który mo

Īna juĪ byáo zaimplementowaü

w komputerze — ukazuj

ąc przy okazji, iĪ pomiĊdzy praktycznymi realizacjami a rozwaĪaniami

teoretycznymi nie istnieje wcale a

Ī tak ogromna przepaĞü, jakby siĊ to mogáo wydawaü. W tym

samym czasie J. H. Morris odkry

á dokáadnie ten sam algorytm jako rozwiązanie problemu, który na-

potka

á podczas praktycznej implementacji edytora tekstu. Algorytm K-M-P — bo tak bĊdziemy go

dalej zwali — jest jednym z przyk

áadów doĞü czĊstych w nauce odkryü równolegáych: z jakichĞ nie-

wiadomych powodów nagle kilku pracuj

ących osobno ludzi dochodzi do tego samego dobrego re-

zultatu. Prawda,

Īe jest w tym coĞ niesamowitego i aĪ siĊ prosi o jakieĞ metafizyczne hipotezy?

Knuth, Morris i Pratt opublikowali swój algorytm dopiero w 1976 roku. W mi

Ċdzyczasie pojawiá

si

Ċ kolejny „cudowny” algorytm, tym razem autorstwa R. S. Boyera i J. S. Moore’a, który okazaá

si

Ċ w pewnych zastosowaniach znacznie szybszy od algorytmu K-M-P. Zostaá on równieĪ rów-

nolegle wynaleziony (odkryty?) przez R. W. Gospera. Oba te algorytmy s

ą jednak doĞü trud-

ne do zrozumienia bez pog

áĊbionej analizy, co utrudniáo ich rozpropagowanie.

W roku 1980 R. M. Karp i M. O. Rabin doszli do wniosku,

Īe przeszukiwanie tekstów nie jest aĪ

tak dalekie od standardowych metod przeszukiwania, i wynale

Ĩli algorytm, który — dziaáając

ci

ągle w czasie proporcjonalnym do

M+N

— jest ideowo zbli

Īony do poznanego juĪ przez nas

algorytmu typu brute-force. Na dodatek jest to algorytm, który mo

Īna wzglĊdnie áatwo uogól-

ni

ü na przypadek poszukiwania w tablicach 2-wymiarowych, co czyni go potencjalnie uĪytecznym

w obróbce obrazów.

W nast

Ċpnych trzech sekcjach szczegóáowo omówimy sobie wspomniane w tym przeglądzie hi-

storycznym algorytmy.

4

Zera i jedynki symbolizuj

ą tu dwa róĪne od siebie znaki.

5

Zwykle

M

b

Ċdzie znacznie mniejsze niĪ

N

.

6

Termin brute-force jeden z moich znajomych

Ğlicznie przetáumaczyá jako „metodĊ mastodonta”.

background image

182

Algorytmy, struktury danych i techniki programowania

Algorytm K-M-P

Wad

ą algorytmu brute-force jest jego czuáoĞü na konfiguracjĊ danych: faászywe restarty są tu

bardzo kosztowne; w analizie tekstu cofamy si

Ċ o caáą dáugoĞü wzorca, zapominając po drodze

wszystko, co przetestowali

Ğmy do tej pory. Narzuca siĊ tu niejako chĊü skorzystania z informa-

cji, które ju

Ī w pewien sposób posiadamy — przecieĪ w nastĊpnym etapie bĊdą wykonywane

cz

ĊĞciowo te same porównania, co poprzednio!

W pewnych szczególnych przypadkach przy znajomo

Ğci struktury analizowanego tekstu moĪliwe

jest ulepszenie algorytmu. Przyk

áadowo: jeĞli wiemy na pewno, iĪ w poszukiwanym wzorcu je-

go pierwszy znak nie pojawia si

Ċ juĪ w nim w ogóle

7

, to w razie restartu nie musimy cofa

ü

wska

Ĩnika

i

o

j-1

pozycji, jak to by

áo poprzednio (patrz listing txt-1.cpp). W tym przypadku

mo

Īemy po prostu zinkrementowaü

i

, wiedz

ąc, Īe ewentualne powtórzenie poszukiwaĔ na

pewno nic by ju

Ī nie daáo. Owszem, moĪna siĊ áatwo zgodziü z twierdzeniem, iĪ tak wyspecja-

lizowane teksty zdarzaj

ą siĊ relatywnie rzadko, jednak powyĪszy przykáad ukazuje, iĪ ewentu-

alne manipulacje algorytmami poszukiwa

Ĕ są ciągle moĪliwe — wystarczy siĊ tylko rozejrzeü.

Idea algorytmu K-M-P polega na wst

Ċpnym zbadaniu wzorca w celu obliczenia liczby pozycji,

o które nale

Īy cofnąü wskaĨnik

i

w przypadku stwierdzenia niezgodno

Ğci badanego tekstu ze

wzorcem. Oczywi

Ğcie moĪna równieĪ rozumowaü w kategoriach przesuwania wzorca do przodu —

rezultat b

Ċdzie ten sam. To wáaĞnie tĊ drugą konwencjĊ bĊdziemy stosowaü dalej. Wiemy juĪ,

Īe powinniĞmy przesuwaü siĊ po badanym tekĞcie nieco inteligentniej niĪ w poprzednim algo-
rytmie. W przypadku zauwa

Īenia niezgodnoĞci na pewnej pozycji

j

wzorca

8

nale

Īy zmodyfikowaü

ten indeks, wykorzystuj

ąc informacjĊ zawartą w juĪ zbadanej „szarej strefie” zgodnoĞci.

Brzmi to wszystko (zapewne) nies

áychanie tajemniczo, pora wiĊc jak najszybciej wyjaĞniü tĊ

spraw

Ċ, aby uniknąü moĪliwych nieporozumieĔ. Popatrzmy w tym celu na rysunek 8.3.

Rysunek 8.3.
Wyszukiwanie
optymalnego
przesuni

Ċcia

w algorytmie K-M-P

Moment niezgodno

Ğci zostaá zaznaczony poprzez narysowanie przerywanej pionowej kreski.

Otó

Ī wyobraĨmy sobie, Īe przesuwamy teraz wzorzec bardzo wolno w prawo, patrząc jedno-

cze

Ğnie na juĪ zbadany tekst — tak aby obserwowaü ewentualne pokrycie siĊ tej czĊĞci wzorca,

która znajduje si

Ċ po lewej stronie przerywanej kreski, z tekstem, który umieszczony jest powyĪej

wzorca. W pewnym momencie mo

Īe okazaü siĊ, Īe nastĊpuje pokrycie obu tych czĊĞci. Zatrzymu-

jemy wówczas przesuwanie i kontynuujemy testowanie (znak po znaku) zgodno

Ğci obu czĊĞci

znajduj

ących siĊ za kreską pionową.

Od czego zale

Īy ewentualne pokrycie siĊ oglądanych fragmentów tekstu i wzorca? OtóĪ doĞü para-

doksalnie badany tekst nie ma tu nic do powiedzenia — je

Ğli moĪna to tak okreĞliü. Informacja

o tym, jaki on by

á, jest ukryta w stwierdzeniu „

j-1

znaków by

áo zgodnych” — w tym sensie

mo

Īna zupeánie o badanym tekĞcie zapomnieü i analizując wyáącznie sam wzorzec, odkryü po-

szukiwane optymalne przesuni

Ċcie. Na tym wáaĞnie spostrzeĪeniu opiera siĊ idea algorytmu K-M-P.

Okazuje si

Ċ, Īe badając samą strukturĊ wzorca, moĪna obliczyü, jak powinniĞmy zmodyfikowaü

indeks

j

w razie stwierdzenia niezgodno

Ğci tekstu ze wzorcem na j-tej pozycji.

Zanim zag

áĊbimy siĊ w wyjaĞnienia na temat obliczania owych przesuniĊü, popatrzmy na efekt

ich dzia

áania na kilku kolejnych przykáadach. Na rysunku 8.4 moĪemy dostrzec, iĪ na siódmej po-

zycji wzorca

9

(którym jest do

Ğü abstrakcyjny ciąg

12341234

) zosta

áa stwierdzona niezgodnoĞü.

7

Przyk

áad: „ABBBBBBB” — znak ‘A’ wystąpiá tylko jeden raz.

8

Lub i w przypadku badanego tekstu.

9

Licz

ąc indeksy tablicy tradycyjnie od zera.

background image

Rozdzia

ä 8.

i Przeszukiwanie tekstów

183

Rysunek 8.4.
Przesuwanie si

Ċ wzorca

w algorytmie K-M-P (1)

Je

Ğli zostawimy indeks

i

w spokoju, to — modyfikuj

ąc wyáącznie

j

— mo

Īemy bez proble-

mu kontynuowa

ü przeszukiwanie. Jakie jest optymalne przesuniĊcie wzorca? ĝlizgając go

wolno w prawo (patrz rysunek 8.4), doprowadzamy w pewnym momencie do na

áoĪenia siĊ cią-

gów

123

przed kresk

ą — caáa strefa niezgodnoĞci zostaáa wyprowadzona na prawo i ewentualne

dalsze testowanie mo

Īe byü kontynuowane!

Analogiczny przyk

áad znajduje siĊ na rysunku 8.5.

Rysunek 8.5.
Przesuwanie si

Ċ wzorca

w algorytmie K-M-P (2)

Tym razem niezgodno

Ğü wystąpiáa na pozycji

j=3

. Dokonuj

ąc — podobnie jak poprzednio —

przesuwania wzorca w prawo, zauwa

Īamy, iĪ jedyne moĪliwe naáoĪenie siĊ znaków wystąpi po

przesuni

Ċciu o dwie pozycje w prawo — czyli dla

j=1

. Dodatkowo okazuje si

Ċ, Īe znaki za

kresk

ą teĪ siĊ pokryáy, ale o tym algorytm dowie siĊ dopiero podczas kolejnego testu zgodnoĞci na

pozycji

i

.

Dla potrzeb algorytmu K-M-P konieczne okazuje si

Ċ wprowadzenie tablicy przesuniĊü

int shift[M]

. Sposób jej zastosowania b

Ċdzie nastĊpujący: jeĞli na pozycji

j

wyst

ąpiáa nie-

zgodno

Ğü znaków, to kolejną wartoĞcią

j

b

Ċdzie

shift[j]

. Nie wnikaj

ąc chwilowo w sposób ini-

cjalizacji tej tablicy (odmiennej oczywi

Ğcie dla kaĪdego wzorca), moĪemy natychmiast podaü

algorytm K-M-P, który w konstrukcji jest niemal dok

áadną kopią algorytmu typu brute-force:

kmp.cpp

int

kmp(char *w, char *t)

{
int

i,j, N=strlen(t);

for

(i=0,j=0; (i<N) && (j<M); i++,j++)

while( (j>=0) && (t[i]!=w[j]) )
j=shift[j];
if

(j==M)

return i-M;
else
return -1;
}

Szczególnym przypadkiem jest wyst

ąpienie niezgodnoĞci na pozycji zerowej: z zaáoĪenia nie-

mo

Īliwe jest tu przesuwanie wzorca w celu uzyskania naáoĪenia siĊ znaków. Z tego powodu

chcemy, aby indeks

j

pozosta

á niezmieniony przy jednoczesnej progresji indeksu

i

. Jest to

mo

Īliwe do uzyskania, jeĞli umówimy siĊ, Īe

shift[0]

zostanie zainicjowany warto

Ğcią

-1

.

Wówczas podczas kolejnej iteracji p

Ċtli

for

nast

ąpi inkrementacja

i

oraz

j

, co wyzeruje nam

j

.

Pozostaje do omówienia sposób konstrukcji tablicy

shift[M]

. Jej obliczenie powinno nast

ąpiü

przed wywo

áaniem funkcji

kmp

, co sugeruje, i

Ī w przypadku wielokrotnego poszukiwania tego

samego wzorca nie musimy ju

Ī powtarzaü inicjacji tej tablicy. Funkcja inicjująca tablicĊ jest

przewrotna — jest ona praktycznie identyczna z

kmp

z t

ą tylko róĪnicą, iĪ algorytm sprawdza

zgodno

Ğü wzorca... z nim samym!

background image

184

Algorytmy, struktury danych i techniki programowania

int

shift[M];

void

init_shifts(char *w)

{
int

i,j;

shift[0]=-1;
for

(i=0,j=-1;i<M-1;i++,j++,shift[i]=j)

while((j>=0)&&(w[i]!=w[j]))
j=shift[j];
}

Sens tego algorytmu jest nast

Ċpujący: tuĪ po inkrementacji

i

i

j

wiemy,

Īe pierwsze

j

znaków wzor-

ca jest zgodne ze znakami na pozycjach:

p[i-j-1]... p[i-1]

(ostatnie

j

pozycji w pierwszych

i

znakach wzorca). Poniewa

Ī jest to najwiĊksze

j

spe

ániające powyĪszy warunek, zatem aby nie

omin

ąü potencjalnego miejsca wykrycia wzorca w tekĞcie, naleĪy ustawiü

shift[i]

na

j

.

Popatrzmy, jaki b

Ċdzie efekt zadziaáania funkcji

init_shifts

na s

áowie ananas (patrz rysunek

8.6). Zacieniowane litery oznaczaj

ą miejsca, w których wystąpiáa niezgodnoĞü wzorca z tekstem.

W ka

Īdym przypadku graficznie przedstawiono efekt przesuniĊcia wzorca — widaü wyraĨnie,

które strefy pokrywaj

ą siĊ przed strefą zacieniowaną (porównaj rysunek 8.5).

Rysunek 8.6.
Optymalne przesuni

Ċcia

wzorca „ananas”
w algorytmie K-M-P

Przypomnijmy jeszcze,

Īe tablica

shift

zawiera now

ą wartoĞü dla indeksu

j

, który przemieszcza si

Ċ

po wzorcu.

Algorytm K-M-P mo

Īna zoptymalizowaü, jeĞli znamy z góry wzorce, których bĊdziemy poszuki-

wa

ü. Przykáadowo: jeĞli bardzo czĊsto zdarza nam siĊ szukaü w tekstach sáowa ananas, to

w funkcj

Ċ

kmp

mo

Īna wbudowaü tablicĊ przesuniĊü:

ananas.cpp

int

kmp_ananas(char *t)

{
int

i=-1;

start: i++;
et0: if (t[i]!='a')
goto start;
i++;
et1: if (t[i]!='n')
goto et0;
i++;
et2: if (t[i]!='a')
goto et0;
i++;
et3: if (t[i]!='n')
goto et1; i++;
if (t[i]!='a')
goto et2; i++;
if (t[i]!='s')
goto et3; i++;
return

i-6;

}

background image

Rozdzia

ä 8.

i Przeszukiwanie tekstów

185

W celu w

áaĞciwego odtworzenia etykiet naleĪy oczywiĞcie co najmniej raz wykonaü funkcjĊ

init_shifts

lub obliczy

ü samemu odpowiednie wartoĞci. W kaĪdym razie gra jest warta

Ğwieczki: powyĪsza funkcja charakteryzuje siĊ bardzo zwiĊzáym kodem wynikowym asemblero-
wym, jest zatem bardzo szybka. Posiadacze kompilatorów, które umo

Īliwiają generacjĊ kodu

wynikowego jako tzw. „assembly output”

10

, mog

ą z áatwoĞcią sprawdziü róĪnice pomiĊdzy

wersjami

kmp

i

kmp_ananas

! Dla przyk

áadu mogĊ podaü, Īe w przypadku wspomnianego kom-

pilatora GNU klasyczna wersja procedury

kmp

(wraz z

init_shifts

) mia

áa objĊtoĞü okoáo 170 linii

kodu asemblerowego, natomiast

kmp_ananas

zmie

Ğciáa siĊ w ok. 100 liniach. (Patrz: pliki z rozsze-

rzeniem s na dyskietce dla kompilatora GNU lub asm dla kompilatora Borland C++ 5.5).

Algorytm K-M-P dzia

áa w czasie proporcjonalnym do

M+N

w najgorszym przypadku. Naj-

wi

Ċkszy zauwaĪalny zysk związany z jego uĪyciem dotyczy przypadku tekstów o wysokim

stopniu samopowtarzalno

Ğci — doĞü rzadko wystĊpujących w praktyce. Dla typowych tekstów

zysk zwi

ązany z wyborem metody K-M-P bĊdzie zatem sáabo zauwaĪalny.

U

Īycie tego algorytmu jest jednak niezbĊdne w tych aplikacjach, w których nastĊpuje liniowe

przegl

ądanie tekstu — bez buforowania. Jak áatwo zauwaĪyü, wskaĨnik

i

w funkcji

kmp

nigdy nie

jest dekrementowany, co oznacza,

Īe plik moĪna przeglądaü od początku do koĔca bez cofania

si

Ċ w nim. W niektórych systemach moĪe to mieü istotne znaczenie praktyczne — przykáadowo:

mamy zamiar analizowa

ü bardzo dáugi plik tekstowy i charakter wykonywanych operacji nie po-

zwala na cofni

Ċcie siĊ w tej czynnoĞci (i w odczytywanym na bieĪąco pliku).

Algorytm Boyera i Moore’a

Kolejny algorytm, który b

Ċdziemy omawiali, jest ideowo znacznie prostszy do zrozumienia niĪ algo-

rytm K-M-P. W przeciwie

Ĕstwie do metody K-M-P porównywaniu ulega ostatni znak wzorca. To

niekonwencjonalne podej

Ğcie niesie ze sobą kilka istotnych zalet:

 JeĞli podczas porównywania okaĪe siĊ, Īe rozpatrywany aktualnie znak nie wchodzi

w ogóle w sk

áad wzorca, wówczas moĪemy „skoczyü” w analizie tekstu o caáą dáugoĞü

wzorca do przodu! Ci

ĊĪar algorytmu przesunąá siĊ wiĊc z analizy ewentualnych zgodnoĞci

na badanie niezgodno

Ğci — a te ostatnie są statystycznie znacznie czĊĞciej spotykane.

 Skoki wzorca są zazwyczaj znacznie wiĊksze od

1

— porównaj z metod

ą K-M-P!

Zanim przejdziemy do szczegó

áowej prezentacji kodu, omówimy sobie na przykáadzie jego dzia-

áanie. Spójrzmy w tym celu na rysunek 8.7, gdzie przedstawione jest poszukiwanie ciągu zna-
ków „lek” w tek

Ğcie „Z pamiĊtnika máodej lekarki

11

.

Pierwsze pi

Ċü porównaĔ trafia na litery:

p

,

i

,

n

,

a

i

đ

, które we wzorcu nie wyst

Ċpują! Za kaĪ-

dym razem mo

Īemy zatem przeskoczyü w tekĞcie o trzy znaki do przodu (dáugoĞü wzorca). Porów-

nanie szóste trafia jednak na liter

Ċ

e

, która w s

áowie „lek” wystĊpuje. Algorytm wówczas prze-

suwa wzorzec o tyle pozycji do przodu, aby litery

e

na

áoĪyáy siĊ na siebie, i porównywanie jest

kontynuowane.

Nast

Ċpnie okazuje siĊ, Īe litera

j

nie wyst

Ċpuje we wzorcu — mamy zatem prawo przesunąü

si

Ċ o kolejne 3 znaki do przodu. W tym momencie trafiamy juĪ na poszukiwane sáowo, co na-

st

Ċpuje po jednokrotnym przesuniĊciu wzorca, tak aby pokryáy siĊ litery

k

.

Algorytm jest, jak wida

ü, klarowny, prosty i szybki. Jego realizacja takĪe nie jest zbyt skompli-

kowana. Podobnie jak w przypadku metody poprzedniej, tak

Īe i tu musimy wykonaü pewną pre-

kompilacj

Ċ w celu stworzenia tablicy przesuniĊü. Tym razem jednak tablica ta bĊdzie miaáa tyle

pozycji, ile jest znaków w alfabecie — wszystkie znaki, które mog

ą wystąpiü w tekĞcie plus spacja.

10

W przypadku kompilatorów popularnej serii Borland C++ nale

Īy skompilowaü program rĊcznie poprzez polecenie

bcc32 -S -Ixxx plik.cpp, gdzie xxx oznacza katalog z plikami typu H; identyczna opcja istnieje w kompilatorze GNU
C++, nale

Īy wystukaü: c++ -S plik.cpp.

11

Tytu

á znakomitego cyklu autorstwa Ewy SzumaĔskiej.

background image

186

Algorytmy, struktury danych i techniki programowania

Rysunek 8.7. Przeszukiwanie tekstu metod

ą Boyera i Moore'a

B

Ċdziemy równieĪ potrzebowali prostej funkcji

indeks

, która zwraca w przypadku spacji licz-

b

Ċ zero — w pozostaáych przypadkach numer litery w alfabecie. PoniĪszy przykáad uwzglĊdnia

jedynie kilka polskich liter — Czytelnik uzupe

áni go z áatwoĞcią o brakujące znaki. Numer litery jest

oczywi

Ğcie zupeánie arbitralny i zaleĪy od programisty. WaĪne jest tylko, aby nie pominąü w ta-

blicy

Īadnej litery, która moĪe wystąpiü w tekĞcie. Jedna z moĪliwych wersji funkcji

indeks

jest

przedstawiona poni

Īej:

bm.cpp

const

int K=26*2+2*2+1;// znaki ASCII + polskie litery + odst

Ċp

int

shift[K];

int

indeks(char c)

{
switch

(c)

{
case ' ':return 0; // odst

Ċp = 0

case 'ú':return 53;
case 'ù':return 54; // polskie litery
case 'đ':return 55;
case 'Đ':return 56; // itd. dla pozosta

áych polskich liter

default:
if(islower(c))
return c-'a'+1;
else
return c-'A'+27;
• •
• •

Funkcja

indeks

ma jedynie charakter us

áugowy. SáuĪy ona m.in. do wáaĞciwej inicjalizacji tablicy

przesuni

Ċü. Mając za sobą analizĊ przykáadu z rysunku 8.7, Czytelnik nie powinien byü zbyt-

nio zdziwiony sposobem inicjalizacji:

void

init_shifts(char *w)

{
int

M=strlen(w);

for

(int i=0; i<K; i++)

shift[i]=M;

background image

Czytaj dalej...

Rozdzia

ä 8.

i Przeszukiwanie tekstów

187

for

(int i=0; i<M; i++)

shift[indeks(w[i])]=M-i-1;
}

Przejd

Ĩmy wreszcie do prezentacji samego listingu algorytmu, z przykáadem wywoáania:

int

bm(char *w, char *t)

{
init_shifts(w);
int

i, j,N=strlen(t),M=strlen(w);

for

(i=M-1,j=M-1; j>0; i--,j--)

while(t[i]!=w[j])
{
int x=shift[indeks(t[i])];
if(M-j>x)
i+=M-j;
else
i+=x;
if (i>=N)
return -1;
j=M-1;
}
return

i;

}

int

main()

{
char

*t="Z pamiútnika mđodej lekarki";

cout << "Wynik poszukiwaē="<<bm("lek",t)<<endl;
}

Algorytm Boyera i Moore’a, podobnie jak i K-M-P, jest klasy

M+N

— jednak jest on o tyle od

niego lepszy, i

Ī w przypadku krótkich wzorców i dáugiego alfabetu koĔczy siĊ po okoáo M/N

porównaniach. W celu obliczenia optymalnych przesuni

Ċü

12

autorzy algorytmu proponuj

ą

skombinowanie powy

Īszego algorytmu z tym zaproponowanym przez Knutha, Morrisa i Pratta.

Celowo

Ğü tego zabiegu wydaje siĊ jednak wątpliwa, gdyĪ optymalizując sam algorytm, moĪna

w bardzo

áatwy sposób uczyniü zbyt czasocháonnym sam proces prekompilacji wzorca.

Algorytm Rabina i Karpa

Ostatni algorytm do przeszukiwania tekstów, który b

Ċdziemy analizowali, wymaga znajomoĞci roz-

dzia

áu 7. i terminologii, która zostaáa w nim przedstawiona.

Algorytm Rabina i Karpa polega bowiem na do

Ğü przewrotnej idei:

 Wzorzec

w

(do odszukania) jest kluczem (patrz terminologia transformacji kluczowej

w rozdziale 7.) o d

áugoĞci

M

znaków, charakteryzuj

ącym siĊ pewną wartoĞcią wybranej

przez nas funkcji

H

. Mo

Īemy zatem obliczyü jednokrotnie

H

w

=H(w)

i korzysta

ü z tego

wyliczenia w sposób ci

ągáy.

 Tekst wejĞciowy

t

(do przeszukania) mo

Īe byü w taki sposób odczytywany, aby na

bie

Īąco znaü

M

ostatnich znaków

13

. Z tych

M

znaków wyliczamy na bie

Īąco

H

t

=H(t)

.

Gdy za

áoĪymy jednoznacznoĞü wybranej funkcji

H

, sprawdzenie zgodno

Ğci wzorca z aktualnie ba-

danym fragmentem tekstu sprowadza si

Ċ do odpowiedzi na pytanie: czy

H

w

jest równe

H

t

? Spo-

strzegawczy Czytelnik ma jednak prawo pokr

Ċciü w tym miejscu z powątpiewaniem gáową:

przecie

Ī to nie ma prawa dziaáaü szybko! Istotnie pomysá wyliczenia dodatkowo funkcji

H

dla

12

Rozwa

Ī np. wielokrotne wystĊpowanie takich samych liter we wzorcu.

13

Na samym pocz

ątku bĊdzie to oczywiĞcie

M

pierwszych znaków tekstu.


Wyszukiwarka

Podobne podstrony:
Algorytmy, struktury danych i techniki programowania Wydanie IV
Algorytmy, struktury danych i techniki programowania wydanie 3
Algorytmy struktury danych i techniki programowania Wydanie III algo3
Algorytmy, struktury danych i techniki programowania wydanie 3
Algorytmy, struktury danych i techniki programowania Wydanie III
Algorytmy struktury danych i techniki programowania Wydanie III algo3
Algorytmy struktury danych i techniki programowania Wydanie III algo3
Algorytmy struktury danych i techniki programowania Wydanie III
Algorytmy struktury danych i techniki programowania Wydanie III algo3
Algorytmy struktury danych i techniki programowania Wydanie III

więcej podobnych podstron