background image

1

Wykład IX

Metody algorytmiczne

Algorytmy i struktury danych
Wyższa Szkoła Biznesu
Semestr  III    Informatyka 
stosowana

background image

2

Wiemy już prawie 

wszystko, ale...

Wydaje  się,  że  możemy  teraz  dalej  pomyślnie  podążać 

naprzód w naszym algorytmicznym znoju.

– Wiemy jaką strukturę mają algorytmy i jak urządzić 

obiekty, którymi manipulują; 

– Wiemy  również,  jak  je  zapisać,  aby  wykonał  je 

komputer (języki programowania). 

– Możemy  zatem  powiedzieć  naszemu  procesorowi, 

co i kiedy ma robić.

Byłaby to jednak nazbyt naiwna ocena sytuacji i wkrótce 

zobaczymy  rozmaite  tego  przyczyny.  Jedna  z  trudności 

tkwi  w  fakcie,  że  nie  podaliśmy  żadnych  metod, 

których dałoby się użyć do wymyślenia algorytmu.

background image

3

Od kawałków do całości

Łatwo się opowiada o  konstrukcjach,  z których 
może  korzystać  algorytm  (czyli  o  kawałkach,  z 
których  może  się  składać),  ale  musimy 
powiedzieć 

nieco 

więcej 

sposobach 

zabierania  się  do  zbudowania  całości  z  tych 
kawałków.
Na  tym  wykładzie  dokonamy  przeglądu  kilku 
całkiem  ogólnych  metod  algorytmicznych, 
których  może  użyć  projektant  do  znalezienia 
rozwiązania zadania algorytmicznego.

background image

4

Tworzenie algorytmów

to zadanie twórcze

Trzeba wszak zauważyć, że na obmyślanie przepisów nie ma 

dobrych  przepisów  Każde  takie  zadanie  stanowi  ambitne 

wyzwanie dla projektanta algorytmów.
Jedne zadania są proste, inne skomplikowane, a w wypadku 

jeszcze  innych  nawet  nadzieja  na  ich  rozwiązanie  jest 

złudna.
Jedyne  zatem  co  możemy  pokazać  to,  że  pewne  algorytmy 

całkiem zgrabnie wypływają z pewnych ogólnych wzorców.
Morałem  niech  będzie  to,  że  projektant  algorytmów 

może  odnieść  korzyść  wypróbowując  właśnie  te 

wzorce  w  pierwszej  kolejności.  Jednak  z  całą  pewnością 

projektowanie  algorytmów  jest  zajęciem  twórczym,  które 

niekiedy może wymagać naprawdę ogromnej pomysłowości.

background image

5

Poszukiwania i wędrówki

W  wielu  zadaniach  algorytmicznych  powstaje  potrzeba 

obejścia wszystkich elementów pewnej struktury.
Czasami  struktura  jest  po  prostu  jedną  z  jawnie 

dostępnych w algorytmie struktur danych;
Innym razem jest to jakaś struktura pośrednio w czymś 

zawarta,  której  być  może  nie  da  się  naprawdę 

„zobaczyć”, ale która kryje się gdzieś pod powierzchnią;
Czasami szuka się w tej strukturze czegoś szczególnego 

(„kto jest kierownikiem działu łączności z klientami?”);
Albo  też  pewną  pracę  należy  wykonać  w  każdym 

punkcie („oblicz średnią ocen wszystkich studentów”).

background image

6

Przykłady

Na  przykład  widać  od  razu,  że  proste  zadanie 

sumowania  zarobków  z  jednego  z  poprzednich 

wykładów wymaga łatwego przejścia po danej liście 

pracowników.
Z drugiej  strony, o zadaniu, które dotyczyło jedynie 

pracowników zarabiających więcej niż ich kierownicy 

(patrz wykład: Algorytmy i dane), można pomyśleć, 

że  wymaga  ono  obejścia  wyimaginowanej  tablicy 

dwuwymiarowej 

wierszach 

kolumnach 

odpowiadających 

poszczególnym 

pracownikom. 

Wykonując  to  obejście  poszukujemy  pewnych  par 

pracownik-kierownik.

background image

7

Dobieramy odpowiednią

metodę obejścia

W  takich  przypadkach  zadanie  polega  na 

znalezieniu  najbardziej  naturalnego  sposobu 

obejścia 

struktury 

danych 

(jawnej 

bądź 

niejawnej),  z  którą  ma  się  do  czynienia, 

i wyprowadzeniu algorytmu właśnie stąd.
Kiedy  w  grę  wchodzą  wektory  i  tablice,  wtedy 

pojawiają się  zwykle  pętle i  zagnieżdżone  pętle, 

co wyjaśniono na wykładzie Algorytmy i dane.
Kiedy w grę wchodzą drzewa, wtedy pojawia się 

rekurencja,  tak  jak  w  przykładzie  z sortowaniem 

drzewiastym (ten sam wykład).

background image

8

Pomysł nie zawsze jest 

oczywisty

Co  prawda,  pomysł  leżący  u  podstaw  algorytmu 

sortowania  drzewiastego  jest  dosyć  subtelny  i  nie 

można  nań  wpaść  wymyślając  po  prostu  najlepszą 

strukturę  sterowania  do  obejścia  danej  struktury 

danych.
Jeśli  już  jednak  trafi  się  na  ten  pomysł,  to 

drobnostką  jest  zdanie  sobie  sprawy  z  tego,  że 

składanie  odwiedzin  za  drugim  przejściem,  w 

którym  elementy  wypisywane  są  według  porządku, 

nie  jest  niczym  więcej  niż  pewnym  sposobem 

obejścia drzewa binarnego, skąd nietrudno dojść do 

konkluzji, że należy użyć rekurencji.

background image

9

Sposoby obchodzenia 

drzew

Obejście  wynikające  z  przyjęcia  rekurencyjnej  trasy 

lewostronnej  (sortowanie  drzewiaste)  bywa  czasami 

nazywane  poszukiwaniem  w  głąb  z  nawracaniem,  jako 

że  procesor  „zanurza  się”  w  drzewo  próbując  dostać  się 

najgłębiej,  jak  można,  a  kiedy  nie  może  już  dalej  iść, 

nawraca  niechętnie,  stale  usiłując  nurkować  na  nowo. 

Jedyną  dodatkowa  cechą  jest  tu  wymaganie,  aby 

nurkować jak najbardziej na lewo.
Są też inne sposoby obchodzenia drzew, z których jeden, 

dualny  wobec  poszukiwania  w  głąb,  otrzymał  miano 

poszukiwania  wszerz.  Obchodzenie  wszerz  oznacza,  że 

wyczerpuje się kolejno poziomy drzewa - najpierw korzeń, 

potem całe jego potomstwo, następnie ich potomstwo itd.

background image

10

Poszukiwanie - 

Przykładowe zadania

Wiele 

ciekawych 

zadań 

algorytmicznych 

obraca się wokół pojęć geometrycznych, takich 
jak  punkty,  linie  i  odległości,  i  stanowi  zatem 
część  obszaru  zagadnień  znanych  jako 
geometria obliczeniowa.
Wiele  problemów  w  tej  dziedzinie  wydaje  się 
wzrokowo 

zwodniczo 

„łatwymi” 

do 

rozwiązania,  stanowią  one  jednak  prawdziwie 
ambitne zadanie dla projektantów algorytmów. 

background image

11

Oto jedno z prostszych 

zadań

Przypuśćmy,  że  mamy  prosty  wielokąt  wypukły 

jak  na  rys.  (następny  slajd)  i  że  interesuje  nas 

znalezienie  takich  dwóch  punktów  na  jego 

obwodzie, które dzieli największa odległość.
Przyjmujemy, że wielokąt jest przedstawiony jako 

ciąg współrzędnych <X,Y> jego wierzchołków, w 

kolejności  zgodnej  z  ruchem  wskazówek  zegara. 

Jako  że  największa  odległość  wystąpi  dla  dwóch 

wierzchołków,  nie  ma  potrzeby  zwracania  uwagi 

na  żadne  inne  punkty  leżące  wzdłuż  boków 

wielokąta różne od wierzchołków.

background image

12

Dwa przykładowe 

wielokąty wypukłe

Banalne  rozwiązanie  polegałoby  na  zbadaniu,  w  jakimś 

porządku, 

wszystkich 

par 

wierzchołków 

przechowywałoby  w  zmiennych  bieżące  maksimum  i 

parę, która to maksimum osiąga.

background image

13

Najprostsze rozwiązanie – 

więcej szczegółów

Dla  każdej  nowej  rozważanej  pary  w  prosty  sposób 

oblicza  się  odległość,  nową  odległość  porównuje  się  z 

bieżącym  maksimum  i  uaktualnia  się  zmienne,  jeśli 

okaże się, że jest ona większa, co oznacza, że stanowi 

właśnie nowe maksimum.
Jeśli rozważymy przez chwilę to rozwiązanie, stanie się 

jasne, że w istocie obchodzimy wyimaginowaną tablicę, 

w  której  każdy  wiersz  i  każda  kolumna  odpowiadają 

pewnemu  wierzchołkowi.  Wyimaginowany  element 

danych  tkwiący  w  pozycji  <I,  J>  tej  tablicy  jest 

odległością między wierzchołkami I a J. Obejścia można 

łatwo  dokonać  przy  pomocy  dwóch  zagnieżdżonych 

pętli.

background image

14

Czy istnieje lepsze 

rozwiązanie ?

W tym rozwiązaniu jednak uwzględnia się o wiele za dużą liczbę 

potencjalnych  par;  czyż  nie  powinno  się  rozważać  tylko 

„przeciwległych” par punktów, takich jak <1,4>, <2,5> i <3,6) 

z rys. a (dwa slajdy wstecz)?
Oznaczałoby  to  obejście  jedynie  wektora  par  „specjalnych”,  a 

nie  tablicy  wszystkich  par,  i  dałoby  oczywiście  w  wyniku 

sprawniejszy algorytm, wymagający zaledwie pojedynczej pętli.
No  cóż,  nie  jest  to  tak  proste,  jak  się  wydaje,  ponieważ 

przeciwległe  pary,  których  sobie  życzymy,  nie  muszą  mieć 

jednakowej  liczby  wierzchołków  po  każdej  stronie;  a  na  tym 

samym  rys.  tylko  w  pkt.  b  pokazano  wielokąt,  w  którym 

maksimum  występuje  dla  sąsiadujących  wierzchołków,  które 

pominąłby  algorytm  rozważający  tylko  pary  o  przeciwległych 

numerach.

background image

15

Lepsze rozwiązanie

w którym rzeczywiście stosuje 

się tylko jedną pętlę i rozważa 

tylko „właściwy” gatunek 

przeciwległych par, 

przedstawiono na rys. obok. 

Najpierw „rysuje” się linię 

prostą wzdłuż krawędzi <1,2>, 

czyli krawędzi między 

wierzchołkami 1 i 2. Następnie 

do wielokąta, z jego drugiej 

strony, stopniowo przysuwa

przysuwa się linię równoległą do poprzedniej dopóty, dopóki 
nie  natrafi  na  jeden  z  wierzchołków.  Za  pierwsze 
przybliżenie  szukanego  maksimum  przyjmuje  się  większą  z 
odległości między tym wierzchołkiem a wierzchołkami 1 i 2.

background image

16

Lepsze rozwiązanie 

(cd.)

Potem  rozpoczyna  się  ruch  w  kierunku  zgodnym  z  obiegiem 

wskazówek  zegara.  Każdy  krok  obejmuje  obrót  jednej  z  dwóch  linii 

tak,  aby  leżała  wzdłuż  krawędzi  następnej  w  tym  kierunku,  i 

dopasowanie drugiej linii tak, aby obie przebiegały równolegle. Którą 

z  linii  obrócić,  a  którą  dopasować,  ustala  się  porównując  wysiłek 

niezbędny do wykonania obrotu; obraca się linię tworzącą mniejszy 

kąt  z  następną  krawędzią.  Po  zakończeniu  obrotu  mamy  nowy 

wierzchołek leżący na właśnie obróconej linii.
Obliczoną  odległość  między  tym  wierzchołkiem  a  wierzchołkiem 

leżącym  na  linii  dopasowanej  porównuje  się,  jak  poprzednio,  z 

bieżącym  maksimum.  Wszystko  to  wykonuje  się  dookoła  całego 

wielokąta.  Wszelkie  akcje  wymagane  przez  ten  algorytm  obejmują 

proste  manipulacje  numeryczne  współrzędnymi  wierzchołków, 

wynikające z elementarnej geometrii analitycznej. Pozostałe rysunki  

ilustrują kolejność przekształceń dokonywanych na tych liniach.

background image

17

W poszukiwaniach 

przydaje się wnikliwość

Wybrano  ten  przykład,  aby  dodatkowo 
zilustrować  fakt,  że  samo  zauważenie 
potrzeby obejścia czegoś i wymyślenie, co 
naprawdę  trzeba  obejść,  jest  ważne  i 
może  stanowić  istotną  pomoc,  ale  nie 
zawsze 

wystarczy 

do 

rozwiązania 

trudniejszych zadań algorytmicznych.
Odrobina  wnikliwości  i  sporo  wiedzy  z 
danej dziedziny nigdy tu nie zawadzą.

background image

18

Dziel i zwyciężaj

Często można uporać się z zadaniem sprowadzając je 
do  mniejszych  zadań  tego  samego  rodzaju  i 
rozwiązując  je.  Później  można  połączyć  te  częściowe 
rozwiązania  w  rozwiązanie  ostateczne  pierwotnego 
zadania.
Jeśli  te  mniejsze  zadania  są  dokładnie  tym  samym 
zadaniem,  z którym  mamy  do  czynienia,  lecz 
postawionym  wobec  „mniejszych”  lub  „prostszych” 
danych,  to  oczywiście  w  algorytmie  można  użyć 
rekurencji.
Nazwa tej metody jest oczywista: dziel i zwyciężaj.

background image

19

„Dziel i zwyciężaj” 

widzieliśmy w przykładzie 

z Wieżami Hanoi

Sam 

pomysł 

widzieliśmy 

już 

pośrednio  w  przykładzie  Wież 
Hanoi:  algorytm  uporał  się  z 
zadaniem 

dla 

krążków 

rozwiązując, 

odpowiednim 

porządku 

kontekście, 

dwa 

zadania dla N-1 krążków.

background image

20

Inny przykład 

zastosowania podziałów i 

zwycięstw

Wyobraź sobie, że wręczono ci książkę telefoniczną o 

porozrzucanych  kartkach  lub  że  -  co  zabrzmi 

poważniej - otrzymałeś nieuporządkowaną listę L. 
Interesuje  nas  nie  posortowanie  listy  L,  lecz  tylko 

znalezienie  jej  najmniejszego  i  największego 

elementu.  Jasne,  że  możemy  po  prostu  raz  przebiec 

tę  listę,  przechowując  w  zmiennych  bieżące 

maksimum  i minimum,  i  cały  czas  porównywać  z 

nimi każdy element.
Algorytm, który teraz podamy, opiera się na strategii 

„dziel  i  zwyciężaj”  i  jest  nieco  lepszy  niż  naiwny 

algorytm wspomniany powyżej.

background image

21

Schemat algorytmu szukania 

minimum i maksimum wg. 

„dziel i zwyciężaj”

(1) jeśli  lista  L  składa  się  z  jednego  elementu,  to  ten 

element  stanowi  zarówno  minimum,  jak  i  maksimum; 

jeśli składa się ona z dwóch elementów, to mniejszy z 

nich stanowi minimum, a większy - maksimum;

(2) w przeciwnym razie wykonaj, co następuje:

(2.1)

podziel listę L na połowy, L

1

 i L

2

;

(2.2)

znajdź ich skrajne elementy MIN1, MAX1, MIN2 

i MAX2;

(2.3)

wybierz mniejszy z MIN1 i MIN2; to właśnie jest 

element minimalny listy L;

(2.4)

wybierz większy z MAX1 i MAX2; to właśnie jest 

element maksymalny listy L.

background image

22

Zastosowanie rekurencji 

wymaga zwrócenia przez 

podprogram wyników

Wiersz  (2.2)  aż  woła  o  wykonanie  go  rekurencyjnie,  jako  że 

występujące tam zadania do rozwiązania są dokładnie zadaniami 

min-i-max dla krótszych list L

1

 i L

2

Zwykła  próba  sklecenia  tej  rekurencji  nastręcza  tylko  pewną 

drobną  trudność:  mianowicie  to,  że  w  tym  miejscu,  w 

przeciwieństwie 

do 

procedury 

Wież 

Hanoi, 

wywołanie 

rekurencyjne  musi  dostarczyć  wyników,  których  trzeba  użyć  w 

dalszej części.
Procesor  musi  w  jakiś  sposób  nie  tylko  zapamiętać  swój  adres 

powrotny  i  to,  jak  przywrócić  w  swym  otoczeniu  sytuację 

poprzedzającą  wyprawienie  się  na  tę  rekurencyjną  wycieczkę, 

ale musi móc przywieźć z powrotem ze swej wyprawy wyniki. W 

tym  przypadku  byłoby  najkorzystniejsze,  gdyby  procesor  mógł 

powrócić  z  rekurencyjnego  wywołania  razem  z  żądanym 

minimum i maksimum.

background image

23

Rekurencyjne min-i-max 

realizujące metodę dziel i 

zwyciężaj

procedura znajdź-min-i-max-w L:
(1)jeśli lista L składa się z jednego elementu, to nadaj MIN i MAX 

właśnie jego wartość; jeśli L składa się z dwóch elementów, to 

nadaj MIN wartość mniejszego z nich, a MAX - większego;

(2)w przeciwnym razie wykonaj co następuje:

(2.1)

podziel listę L na połowy, L

1

 i L

2

;

(2.2)

wywołaj  znajdź-min-i-max-w  L

1

;  umieszczając 

otrzymane w wyniku wartości w MIN1 i MAX1;

(2.3)

wywołaj  znajdź-min-i-max-w  L

2

,  umieszczając 

otrzymane w wyniku wartości w MIN2 i MAX2;

(2.4)

nadaj MIN mniejszą wartość z MIN1 i MIN2;

(2.5)

nadaj MAX większą wartość z MAX1 i MAX2;

(3)

wróć z wartościami MIN i MAX.

background image

24

Nie tylko min i max

Zastosowanie  paradygmatu  „dziel  i  zwyciężaj”  może 

przynieść  wiele  dobrego  przy  sortowaniu  listy,  a  nie 

tylko przy znajdowaniu w niej skrajnych elementów. 
Aby  posortować  listę  L  zawierającą  co  najmniej  dwa 

elementy,  możemy  podobnie  podzielić  ją  na  połówki  L

1

 

L

2

 i posortować je rekurencyjnie.

Przypadek 

pojedynczego 

elementu 

traktuje 

się 

oddzielnie, tak jak w przykładzie z min-i-max.
Aby  otrzymać  ostatecznie  posortowaną  wersję  listy  L, 

dalej  możemy  scalić  posortowane  połówki.  Aby  scalić 

dwie  posortowane  listy,  w  kółko  wybieramy  i  dołączamy 

do  listy  wynikowej  mniejszy  z  dwóch  elementów  akurat 

znajdujących się na obu początkach list.s

background image

Sposób działania 

algorytmu 

sortowania przez 

scalanie

 

Sortowanie  przez 
scalanie 

jest 

znacznie 

lepsze 

zarówno 

od 

sortowania 
bąbelkowego,  jak 
i drzewiastego.

background image

26

Zachłanne algorytmy i 

budowniczowie kolei

Wiele  zadań  algorytmicznych  wymaga  dostarczenia  wyniku, 

który jest w jakimś sensie najlepszy z odpowiedniego zestawu 

możliwości.
Rozważmy  teraz  sieć  miast  i  leniwego  przedsiębiorcę 

budującego kolej. Przedsiębiorcy zapłacono za takie ułożenie 

torów, aby z każdego miasta można było dotrzeć do każdego 

innego.
W  umowie  nie  ustalono  żadnych  kryteriów,  takich  jak 

potrzeba  wykonania  pewnych  bezpośrednich  połączeń  czy 

największa  dopuszczalna  liczba  miast  leżących  na  drodze 

między każdymi dwoma.
Naszego  przedsiębiorcę,  jako  że  jest  leniwy,  interesuje 

ułożenie  najtańszej  (czyli  najkrótszej)  kombinacji  odcinków 

szyn.

background image

27

Założenia

Przyjmijmy,  że  z  przyczyn  obiektywnych,  takich  jak 
przeszkody  naturalne,  nie  każde  miasto  można 
połączyć 

bezpośrednimi 

odcinkami 

szyn 

ze 

wszystkimi innymi i że odległości podano jedynie dla 
par miast możliwych do bezpośredniego połączenia.
Przyjmijmy 

dalej, 

że 

koszt 

bezpośredniego 

połączenia miasta z miastem jest proporcjonalny 
do  odległości  między  nimi.  Co  więcej,  nie 
dopuszczamy 

skrzyżowań 

rozjazdów 

poza 

miastami.

background image

28

Grafy

Taka 

sieć 

nazywa 

się 

grafem 

etykietowanym lub krócej grafem.
Grafy przypominają drzewa, tyle że drzewa 
nie  mogą  „zrastać  się”  gałęziami,  a  więc 
nie  mogą  zawierać  cykli  albo  pętli, 
podczas gdy grafy mogą. 
Przykład grafu miast i jego minimalnej sieci 
kolejowej  widzimy  na  rys.  na  następnym 
slajdzie.

background image

29

Sieć miast i jej 

minimalna rozpinająca 

sieć kolejowa

(Rysunek nie zachowuje proporcji)           Całkowity koszt 63 

background image

30

Minimalne drzewo 

rozpinające

Zauważmy,  że  budowniczemu  zależy  naprawdę  na  tym, 

co nazywamy minimalnym drzewem rozpinającym.
To  takie  drzewo,  na  którym  jest  „rozpięty”  graf  w  tym 

sensie,  że  dociera  ono  do  dokładnie  każdego  z  węzłów 

(w  naszym  przypadku  miast)  i  które  jest  najtańszym  z 

takich drzew w tym sensie, że suma etykiet znajdujących 

się przy krawędziach jest najmniejsza z możliwych.
Żądane  rozwiązanie  musi  być  oczywiście  drzewem

gdyż w przeciwnym razie musiałoby zawierać jakiś cykl, 

a leniwy przedsiębiorca mógłby otrzymać tańszą sieć 

kolejową, 

nadal 

łączącą 

wszystkie 

miasta, 

eliminując jeden z odcinków tego cyklu.

background image

31

Metoda zachłanna

Jest  pewne  algorytmiczne  podejście  do  takich  zadań, 

zwane zachłanną metodą.
Zaleca  ono  konstruowanie  minimalnego  drzewa 

rozpinającego  krawędź  po  krawędzi,  w  drodze 

wybierania jako następnej krawędzi zawsze tej, która 

jest najlepsza z punktu widzenia bieżącej sytuacji.
Naprawdę  oznacza  to  przyjęcie  filozofii  rodzaju 

„jedzmy  i  pijmy  dziś,  bo  jutro  może  już  nas  nie 

będzie”.
W  przypadku  rozpinających  sieci  kolejowych  można 

prowadzić  tą  konstrukcję  na  przykład  tak,  jak 

pokazano na rys.

background image

32

Działanie zachłannego 

algorytmu znajdowania min. 

drzewa rozpinającego

background image

33

Sposób poszukiwania 

minimalnego drzewa 

rozpinającego

Zacznijmy 

od 

zdegenerowanego 

drzewa 

składającego się z najtańszej krawędzi grafu. 
W  każdym  kroku  rozbudowujemy  skonstruowane 

dotychczas  drzewo,  dodając  do  niego  najtańszą  z 

krawędzi nie wziętych dotąd pod uwagę, które dają 

nowe drzewo (w szczególności dodanie tej krawędzi 

nie  powinno  doprowadzić  do  powstania  cyklu;  jeśli 

doprowadza  do  tego,  przechodzimy  do  krawędzi 

następnej w porządku rosnących kosztów).
Można wykazać, że ta prosta strategia w rezultacie 

rzeczywiście daje minimalne drzewo rozpinające.

background image

34

Zachłanność nie zawsze 

się opłaca

Zachłanne 

algorytmy 

istnieją 

dla 

ogromnego  mnóstwa  ciekawych  zadań 

algorytmicznych.
Zwykle  można  je  łatwo  wynaleźć  i  w 

niektórych  przypadkach  bywają  bardzo 

intuicyjne.
Trudna  część  polega  zwykle  na  pokazaniu, 

że  zachłanna  strategia  rzeczywiście  działa, 

a jak  zobaczymy  za  chwilę,  są  sytuacje,  w 

których zachłanność zupełnie nie popłaca.

background image

35

Dynamiczne planowanie

i znużeni wędrowcy

Oto  zadanie  podobne  do  znajdowania  minimalnego  drzewa 

rozpinającego, 

ale 

opierające 

się 

zachłannym 

próbom 

rozwiązania.
Dotyczy  ono  również  sieci  miast,  ale  zamiast  leniwego 

budowniczego  kolei  mamy  tu  znużonego  wędrowca,  który  musi 

dostać się z jednego miasta do drugiego.
Chociaż  obaj  mają  przed  sobą  zadanie  do  wykonania  i  chcą 

zminimalizować  jego  ogólny  koszt,  jest  między  nimi  zasadnicza 

różnica: podczas gdy budowniczy ma połączyć siecią torów 

wszystkie  miasta,  wędrowiec  odwiedzi  na  ogół  tylko 

niektóre z nich.
Jest  zatem  jasne,  że  wędrowiec  nie  szuka  minimalnego  drzewa 

rozpinającego. Szuka raczej minimalnej ścieżki, czyli najtańszej 

drogi wiodącej od początkowego miasta do miasta przeznaczenia.

background image

36

Założenia: graf jest 

skierowany, spójny i 

acykliczny

Aby  ułatwić  sobie  prezentację  tego  zadania, 

przyjmiemy,  że  wszystkie  linie  w  grafie  miast  są 

skierowane,  co  oznacza,  że  jeśli  istnieje  jakieś 

bezpośrednie  połączenie  między  tymi  dwoma 

miastami, jest to połączenie w jedną stronę. 
Przyjmiemy  też,  że  graf  jest  spójny,  co  znaczy,  że  z 

każdego  miasta  można  w  końcu  dotrzeć  do  każdego 

innego.
Założymy dalej, że graf miast nie zawiera cykli, tak że 

naprawdę znużony wędrowiec nie będzie narażony na 

błąkanie się w kółko, gdyż takich kółek po prostu nie 

ma. Nasz układ - to skierowany graf acykliczny.

background image

37

Zachłanne podejście do 

zadania

Mamy zatem taki graf i wędrowiec ma dotrzeć z miasta 
do B.
Zachłanne  podejście  do  tego  zadania  dałoby  w  wyniku 
następującą ścieżkę. Zaczęlibyśmy od i dodawalibyśmy 
ciągle,  aż  do  osiągnięcia  docelowego  miasta  B,  do 
bieżącej 

częściowej 

ścieżki 

najtańszą 

krawędź 

wychodzącą  z  właśnie  osiągniętego  miasta  i  prowadzącą 
do miasta jeszcze nie odwiedzonego.
Przykład zastosowania tego robiącego naturalne wrażenie 
algorytmu  do  grafu,  w  którym  koszt  minimalnej  ścieżki 
między  A  i  B  wynosi  13  jednostek,  pokazano  na  rys. 
(następny slajd).

background image

38

Algorytm musi być 

bardziej wnikliwy

Nasz  zachłanny  algorytm  znajduje  ścieżkę  o  koszcie  15 

jednostek, co jest nieco gorsze. Zachłanność w tym miejscu 

nie  popłaca,  jako  że  sprytny  algorytm  musi  być 

dostatecznie  wnikliwy,  aby  wybrać  krawędź  o  długości  5 

prowadzącą do C, a potem krawędź o długości 3 do E, mimo 

że taki wybór lokalnie nie wygląda najbardziej obiecująco.

background image

39

Planowanie dynamiczne

Inna,  już  nie  zachłanna,  metoda 
algorytmiczna,  zwana  planowaniem 
dynamicznym
,  sprawia,  że  można 
dokonywać  tak  subtelnych  wyborów. 
Planowanie dynamiczne opiera się na 
wysubtelnieniu 

dość 

zgrubnego 

kryterium 

używanego 

przy 

natychmiastowej zachłanności. 

background image

40

Idea planowania 

dynamicznego

Tę  metodę  można  opisać  abstrakcyjnie  w  następujący 

sposób.
Przypuśćmy, 

że 

rozwiązanie 

pewnego 

zadania 

algorytmicznego  ma  składać  się  z optymalnego  ciągu 

wyborów.  Jak  już  wykazaliśmy,  całkiem  możliwe,  że 

wybieranie  z  każdego  lokalnego  ciągu  możliwości 

najbardziej  obiecującego  wariantu  nie  doprowadzi  do 

powstania ciągu optymalnego.
Jednak  często  zdarza  się,  że  można  otrzymać  ciąg 

optymalny rozważając wszystkie kombinacje powstałe z:

a) dokonania konkretnego wyboru
b) znalezienia optymalnej części ciągu pozostałych wyborów.

background image

41

Trzeba poszukać kilku 

ścieżek i je porównać

Na przykład na rys. długość najkrótszej ścieżki wiodącej 

z  A  do  B  jest  najmniejszą  z  trzech  liczb  otrzymanych 

przez  wybranie  najpierw  jednego  z  miast  C,  G  i  

(bezpośrednio  osiągalnych  z  A),  a  potem  dodanie  jego 

odległości  od  A  do  długości  najkrótszej  ścieżki 

prowadzącej zeń do B. 
Oznaczywszy długość najkrótszej ścieżki z do B przez 

L(X), możemy symbolicznie napisać

L(A)=minimum z 5+L(C), 14+L(G), 3+L(D)

(zob.  rys.)  Oznacza  to,  że  możemy  znaleźć  ścieżkę 

optymalną znajdując trzy „mniejsze” ścieżki optymalne, 

a później wykonując kilka dodawań i porównań.

background image

42

Ten proces można 

kontynuować

L(D)=minimum z 7+L(E), 6+L(G), 11+L(C)

Kiedy  już  taki  wywód  doprowadzi  do  wyrażeń 

zawierających  L(B)  (tj.  minimalną  odległość  z B  do 

siebie samego), nie trzeba nam niczego więcej, gdyż 

nawet całkiem wyczerpany wędrowiec wie, że L(B)=0, 

na  mocy  faktu,  że  najlepszym  sposobem  dojścia  z  B 

do B jest po prostu pozostanie tam, gdzie się stoi.
Te  spostrzeżenia  prowadzą  do  algorytmu  planowania 

dynamicznego 

rozwiązującego 

ogólne 

zadanie 

znużonego  wędrowca  (czasami  nazywane  zadaniem 

znajdowania najkrótszej ścieżki).

background image

43

Ogólne rozwiązanie 

zadania poszukiwania 

najkrótszej ścieżki

Jeśli  miastami  są  C

1

,  ...,  C

N

,  a  droga  ma  się 

rozpocząć  w  C

1

  i  skończyć  w  C

N

,  to  algorytm 

wymaga  obliczenia  optymalnej  drogi  częściowej 

L(C

I

),  przedstawiającej  najkrótszą  ścieżkę  z  C

I

  do 

miasta docelowego C

N

 dla każdego I między 1 a N

Jednak  L(C

I

)  stanowi  minimum  ze  wszystkich  sum 

postaci

odległość-z-C

I

-do-C

K

 +L(C

K

)

przy  czym  w  obliczaniu  minimum  uwzględnia  się 

wszystkie  miasta  C

K

,  do  których  prowadzą 

bezpośrednio krawędzie z C

I

.

background image

44

Ogólne rozwiązanie 

zadania poszukiwania 

najkrótszej ścieżki 

(cd.)

Ponieważ wszystkie krawędzie są jednokierunkowe, 

a  graf  nie  zawiera  cykli,  możemy  w  ten  sposób 

obliczyć wszystkie L(C

I

) posuwając się z B do tyłu.

Patrząc na rysunek obliczamy najpierw wprost L(F)

L(E)  i  L(G)  i  otrzymujemy  odpowiednio  7,  5  i  6; 

potem  obliczamy  L(C)  jako  minimum  z  2+L(F)  i 

3+L(E)  (w  tym  wypadku  8);  potem  L(D)  itd.,  aż 

wreszcie 

otrzymamy 

L(A)

Równocześnie 

powinniśmy  śledzić  tak  skonstruowaną  ścieżkę 

biegnącą  do  tyłu  z  B  do  A,  jest  bowiem  ścieżką 

optymalna, której szukamy.

background image

45

Narzucająca się tu 
rekurencja nie jest 

optymalnym 

rozwiązaniem

Pojawia  się  pokusa  napisania  rekurencyjnej  procedury  opartej 

na obliczaniu L(C

I

) na podstawie L(C

K

) tak, jak opisano powyżej.

A  jednak  byłoby  to  błędem.  Powód  jest  taki,  że  ta  sama 

optymalna ścieżka częściowa może być potrzebna w więcej niż 

jednej  „większej”  ścieżce,  a  naiwny  algorytm  rekurencyjny 

będzie obliczać ją wciąż na nowo.
Na rys.odległości L(E) używa się do obliczania zarówno L(C) jak 

i  L(D),  a  zatem  procesor  wykonujący  naszkicowaną  przed 

chwilą procedurę obliczy tę ścieżkę dwukrotnie.
W  planowaniu  dynamicznym  będzie  właściwe  iteracyjne 

obliczenie  wszystkich  L(C

I

)  w  porządku  wstecznym  i 

przechowanie  ich  w  tablicy,  tak  że  gdy  raz  już  obliczy  się, 

powiedzmy, odległość L(E), to można jej będzie użyć powtórnie 

wyszukując tę wartość w tablicy, a nie obliczając ją na nowo.

background image

46

To nic innego jak daleko 

posunięta metoda „Dziel i 

zwyciężaj”

O  planowaniu  dynamicznym  można  myśleć  jako  o 

strategii  „dziel  i  zwyciężaj”  posuniętej  do 

ostateczności: 

wszystkie 

zadania 

częściowe 

rozwiązuje się w kolejności wzrastania ich rozmiaru, 

a  wyniki  przechowuje  się  w  jakiejś  strukturze 

danych,  tak  aby  ułatwić  otrzymanie  rozwiązań 

większych zadań.
Tę  metodę  można  stosować  do  wielu  znacznie 

bardziej 

wymyślnych 

zadań, 

które 

do 

przechowywania  częściowych  wyników  wymagają 

struktur  danych  bardziej  złożonych  niż  zwykłe 

wektory.

background image

47

Metody algorytmiczne 

-podsumowanie

Jest  niewiele  powszechnie  przyjętych  wzorców 

postępowania,  na  tyle  ogólnych,  aby  można  je 

było nazwać metodami algorytmicznymi.
Większość lepiej znanych metod już opisaliśmy. 
Mimo  to,  nawet  bez  stawiania  sobie  za 

konkretny 

cel 

uzyskania 

ogólnych 

paradygmatów,  informatycy  wciąż  poszukują 

lepszych  metod  rozwiązywania  coraz  to 

trudniejszych zadań algorytmicznych. 


Document Outline