|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Menu
Distributions (131)
bootable [55]
commercial [7] no-commercial [42] unclassified [20] [7]
Software (10844)
|
Grafy a grafové algoritmy ITento článek pojednává o grafech a algoritmech s nimi spojenými. Článek začíná teorií, ve které jsou zavedeny některé důležité pojmy, včetně definice samotného grafu. Poté následuje algoritmus pro zjištění metriky grafu a nakonec je popsán a ukázán Dijkstrův algoritmus, který slouží k nalezení nejkratších cest.
Úvod do teorie - základní pojmy
Hned v úvodu začneme tím, co je to vlastně graf. Jednoduše řečeno, není to nic jiného, než množina vrcholů a hran,
přičemž jednotlivé vrcholy jsou navzájem hranami spojeny a to tak, že každá hrana spojuje právě dva vrcholy. Je však
nutné popsat graf trochu lépe, proto tedy správná definice grafu zní:
Metrika grafu
Dříve, než přejdeme k samotnému algoritmu, který je mimochodem poměrně jednoduchý, řekneme si, co to vlastně metrika grafu je.
Není to nic jiného, než soubor vzdáleností mezi všemi dvojicemi vrcholů grafu. Je to tedy vlastně matice (dvourozměrné pole),
kde prvek d[i][j] udává vzdálenost mezi vrcholy i a j. Pro názornost si uvedeme ukázku metriky grafu z předchozího
obrázku.
Abychom mohli napsat algoritmus, který nám dokáže metriku vypočítat, musíme nejprve do programu jako vstup zadat náš graf. Otázka je tedy jasná - jakým způsobem reprezentovat graf v paměti? Možnost není jen jedna, v tomto případě však bude nejvhodnější použít tzv. matici sousednosti. Jedná se o dvourozměrné pole (např. G), ve kterém G[i][j] = x znamená, že mezi vrcholy i a j je hrana délky x. Tam, kde hrana není, bude hodnota 0. V jazyce C++ bychom matici sousednosti zapsali takto: const int N = 7; // pocet vrcholu int G[N][N]; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) G[i][j] = 0; G[0][2] = G[2][0] = 15; G[0][4] = G[4][0] = 2; G[0][5] = G[5][0] = 9; G[1][2] = G[2][1] = 7; G[1][4] = G[4][1] = 4; G[1][5] = G[5][1] = 6; G[2][3] = G[3][2] = 12; G[2][6] = G[6][2] = 1; G[3][6] = G[6][3] = 5; Budeme počítat metriku, čili matici, proto potřebujeme pole, které si označíme například d. Na začátku bude hodnota prvku d[i][j] udávat délku hrany mezi vrcholy i a j, nebo "nekonečno" v případě, že hrana mezi těmito vrcholy není (nekonečno budeme reprezentovat konstantou INT_MAX / 2). Poté při každém dalším kroku algoritmu se vzdálenost mezi vrcholy buď zmenší, nebo zůstane stejná (nikdy se nezvětší). My vlastně v každém kroku přidáme jeden konkrétní vrchol a zjistíme, zda je možné se díky tomuto vrcholu dostat z i do j kratší cestou. // vypocet metriky for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (i == j) d[i][j] = 0; else if (G[i][j] != 0) d[i][j] = G[i][j]; else d[i][j] = INT_MAX / 2; for (int k = 0; k < N; k++) for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]); Trochu složitější je to v následujících třech cyklech. Iterační proměnná k nám vlastně symbolizuje nějaký vrchol, i a j jsou vrcholy, mezi kterými hledáme nejkratší cestu. My poté buď ponecháme vzdálenost mezi vrcholy takovou, jaká je momentálně v matici metriky, nebo na pozici d[i][j] uložíme cestu kratší, která vede právě přes vrchol k. Pro dokonalé pochopení doporučuji si algoritmus trochu projít, není nikterak složitý. Až algoritmus skončí, máme v poli d uloženou metriku grafu, čili soubor vzdáleností mezi všemi vrcholy. Podíváme-li se na algoritmus z trochu jiného pohledu, musíme konstatovat, že implementačně není příliš složitý a zjistí nám nejkratší cesty mezi všemi vrcholy. Má však jednu (a poměrně velkou) nevýhodu - obvykle potřebujeme znát nejkratší cestu pouze mezi dvěma konrétními vrcholy, ale tento algoritmus nám vypočítá nejkratší cesty mezi všemi vrcholy, což je dosti zbytečné. Proto se tento algoritmus používá opravdu jen v konkrétních případech, kdy nás zajímají všechny vzdálenosti. V případě, že nás zajímají pouze nejkratší cesty z jednoho konkrétního vrcholu, je mnohem lepší použít Dijkstrův algoritmus. Dijkstrův algoritmusTento algoritmus je sice o něco složitejší než výše uvedený, je však rychlejší. Na základě tohoto algoritmu je možné napsat program, který bude vyhledávat vlakové či autobusové spojení. Nyní se podívejme na popis, jak algoritmus funguje. Všechny vrcholy jsou uchovávány v prioritní frontě, přičemž jsou řazené dle vzdálenosti od zdroje (zdrojem rozumíme vrchol, ze kterého hledáme cesty). V prvním průchodu má pouze zdroj vzdálenost 0, ostatní uzly mají zatím vzdálenost nekonečno (INT_MAX). Poté algoritmus vybere z fronty vrchol, který má nejvyšší prioritu, čili vrchol, který má nejmenší vzdálenost od zdroje. Tento vrchol zařadí mezi zpracované vrcholy. Následně projde všechny nezpracované sousední vrcholy tohoto zpracovaného vrcholu, přidá je do fronty a ověří, zda jsou blíže ke zdroji, než byly předtím. V případě, že zjistí, že některý sousední vrchol je nyní blíže, nastaví tomuto vrcholu novou, menší vzdálenost. Po průchodu všech sousedů se algoritmus vrací zpět na začátek. Algoritmus skončí, jakmile jsou jako zpracované označené všechny vrcholy. Poté máme k dispozici délky nejkratších cest z vrcholu, který jsme si vybrali jako zdroj. Ukázku, jak napsat Dijkstrův algoritmus v jazyce C++, si můžete stáhnout zde. Graf je opět reprezentován pomocí matice sousednosti. Výstupem z programu jsou nejkratší vzdálenosti z vrcholu 0 do všech ostatních vrcholů (v kódu je graf, u kterého jsme výše počítali metriku). To, že algoritmus funguje správně, si tak můžete ověřit tím, že se podíváte na výstup programu a zjistíte, že je totožný s prvním (lepší je možná nultým, neboť hledáme cesty z vrcholu 0) řádkem (nebo sloupcem) matice metriky.
Related article
C/C++ (1) - Úvod C/C++ (2) - První program C/C++ (3) - Proměnné a konstanty C/C++ (4) - Funkce printf C/C++ (5) - Funkce printf podruhé C/C++ (6) - Operátory C/C++ (7) - Podmínka C/C++ (8) - Cykly C/C++ (9) - Pole C/C++ (10) - Standardní vstup a výstup C/C++ (11) - Čtení a konverze čísel C/C++ (12) - Preprocesor C/C++ (13) - Preprocesor podruhé C/C++ (14) - Funkce C/C++ (15) - Proměnné C/C++ (16) - Hlavičkové soubory C/C++ (17) - Makefile C/C++ (18) - Makefile podruhé C/C++ (19) - Příkaz switch a bitové operátory C/C++ (20) - Alokace paměti C/C++ (21) - Práce s řetězci C/C++ (22) - Struktury C/C++ (23) - Seznam C/C++ (24) - Soubory C/C++ (25) - Funkce s proměnným počtem parametrů C/C++ (26) - Standardní knihovna C/C++ (27) - Standardní knihovna podruhé C/C++ (28) - Standardní knihovna potřetí C/C++ (29) - Standardní knihovna počtvrté C/C++ (30) - Výčtový typ a nestandardní knihovny C/C++ (31) - Jazyk C++, historie, charakteristika, vztah k C C/C++ (32) - Omezení C++ oproti C C/C++ (33) - Rozdíly mezi C a C++ C/C++ (34) - Drobná vylepšení C++ C/C++ (35) - Reference, funkce C/C++ (36) - Prostory jmen C/C++ (37) - Prostory jmen podruhé C/C++ (38) - Prostory jmen potřetí C/C++ (39) - Objektově orientované programování C/C++ (40) - Dědičnost a virtuální metody GCC vs. CLANG C++ Binární vyhledávací stromy C++ Datová struktura zásobník C++ - Hashování C++ - Vyhledávání v textu - Brute Force algoritmus C++ šablony Grafy a grafové algoritmy II C++ výjimky C++ Funktory neboli funkční objekty Grafy a grafové algoritmy III. C++ a garbage collector Previous Show category (serial) Next
|
Szukanie oprogramowania
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
©Pavel Kysilka - 2003-2024 | maillinuxsoft.cz | Design: www.megadesign.cz |